mirror of
https://gitlab.com/sortix/sortix.git
synced 2023-02-13 20:55:38 -05:00
Add login(8).
This commit is contained in:
parent
02d5dddc5b
commit
9a1786f688
17 changed files with 2003 additions and 106 deletions
1
Makefile
1
Makefile
|
@ -18,6 +18,7 @@ games \
|
|||
init \
|
||||
kblayout \
|
||||
kblayout-compiler \
|
||||
login \
|
||||
mbr \
|
||||
mkinitrd \
|
||||
regress \
|
||||
|
|
3
login/.gitignore
vendored
Normal file
3
login/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
login
|
||||
*.o
|
||||
arrow.inc
|
41
login/Makefile
Normal file
41
login/Makefile
Normal file
|
@ -0,0 +1,41 @@
|
|||
include ../build-aux/platform.mak
|
||||
include ../build-aux/compiler.mak
|
||||
include ../build-aux/version.mak
|
||||
include ../build-aux/dirs.mak
|
||||
|
||||
OPTLEVEL?=$(DEFAULT_OPTLEVEL)
|
||||
CFLAGS?=$(OPTLEVEL)
|
||||
|
||||
CPPFLAGS:=$(CPPFLAGS) -DVERSIONSTR=\"$(VERSION)\"
|
||||
CFLAGS:=$(CXXFLAGS) -Wall -Wextra
|
||||
|
||||
BINARY=login
|
||||
|
||||
OBJS=\
|
||||
framebuffer.o \
|
||||
graphical.o \
|
||||
login.o \
|
||||
pixel.o \
|
||||
vgafont.o \
|
||||
|
||||
all: $(BINARY)
|
||||
|
||||
.PHONY: all install clean
|
||||
|
||||
$(BINARY): $(OBJS)
|
||||
$(CC) $(OBJS) -o $(BINARY) $(CXXFLAGS) $(LIBS)
|
||||
|
||||
%.o: %.c arrow.inc
|
||||
$(CC) -std=gnu11 $(CPPFLAGS) $(CFLAGS) -c $< -o $@
|
||||
|
||||
arrow.inc: arrow.rgb
|
||||
carray -cs --identifier=arrow arrow.rgb -o $@
|
||||
|
||||
install: all
|
||||
mkdir -p $(DESTDIR)$(SBINDIR)
|
||||
install $(BINARY) $(DESTDIR)$(SBINDIR)
|
||||
mkdir -p $(DESTDIR)$(MANDIR)/man8
|
||||
cp login.8 $(DESTDIR)$(MANDIR)/man8/login.8
|
||||
|
||||
clean:
|
||||
rm -f $(BINARY) $(OBJS) *.o arrow.inc
|
BIN
login/arrow.rgb
Normal file
BIN
login/arrow.rgb
Normal file
Binary file not shown.
176
login/framebuffer.c
Normal file
176
login/framebuffer.c
Normal file
|
@ -0,0 +1,176 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2014, 2015.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation, either version 3 of the License, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
framebuffer.c
|
||||
Framebuffer utilities.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "framebuffer.h"
|
||||
#include "pixel.h"
|
||||
#include "vgafont.h"
|
||||
|
||||
struct framebuffer framebuffer_crop(struct framebuffer fb,
|
||||
size_t left,
|
||||
size_t top,
|
||||
size_t width,
|
||||
size_t height)
|
||||
{
|
||||
// Crop the framebuffer horizontally.
|
||||
if ( fb.xres < left )
|
||||
left = fb.xres;
|
||||
fb.buffer += left;
|
||||
fb.xres -= left;
|
||||
if ( width < fb.xres )
|
||||
fb.xres = width;
|
||||
|
||||
// Crop the framebuffer vertically.
|
||||
if ( fb.yres < top )
|
||||
top = fb.yres;
|
||||
fb.buffer += top * fb.pitch;
|
||||
fb.yres -= top;
|
||||
if ( height < fb.yres )
|
||||
fb.yres = height;
|
||||
|
||||
return fb;
|
||||
}
|
||||
|
||||
void framebuffer_copy_to_framebuffer(const struct framebuffer dst,
|
||||
const struct framebuffer src)
|
||||
{
|
||||
for ( size_t y = 0; y < src.yres; y++ )
|
||||
for ( size_t x = 0; x < src.xres; x++ )
|
||||
framebuffer_set_pixel(dst, x, y, framebuffer_get_pixel(src, x, y));
|
||||
}
|
||||
|
||||
void framebuffer_copy_to_framebuffer_blend(const struct framebuffer dst,
|
||||
const struct framebuffer src)
|
||||
{
|
||||
for ( size_t y = 0; y < src.yres; y++ )
|
||||
{
|
||||
for ( size_t x = 0; x < src.xres; x++ )
|
||||
{
|
||||
uint32_t bg = framebuffer_get_pixel(dst, x, y);
|
||||
uint32_t fg = framebuffer_get_pixel(src, x, y);
|
||||
framebuffer_set_pixel(dst, x, y, blend_pixel(bg, fg));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct framebuffer framebuffer_crop_int(struct framebuffer fb,
|
||||
int left,
|
||||
int top,
|
||||
int width,
|
||||
int height)
|
||||
{
|
||||
if ( left < 0 ) { width -= -left; left = 0; }
|
||||
if ( top < 0 ) { top -= -height; top -= 0; }
|
||||
if ( width < 0 ) { width = 0; }
|
||||
if ( height < 0 ) { height = 0; }
|
||||
return framebuffer_crop(fb, left, top, width, height);
|
||||
}
|
||||
|
||||
struct framebuffer framebuffer_cut_left_x(struct framebuffer fb, int offset)
|
||||
{
|
||||
fb = framebuffer_crop_int(fb, offset, 0, fb.xres - offset, fb.yres);
|
||||
return fb;
|
||||
}
|
||||
|
||||
struct framebuffer framebuffer_cut_right_x(struct framebuffer fb, int offset)
|
||||
{
|
||||
fb = framebuffer_crop_int(fb, 0, 0, fb.xres - offset, fb.yres);
|
||||
return fb;
|
||||
}
|
||||
|
||||
struct framebuffer framebuffer_cut_top_y(struct framebuffer fb, int offset)
|
||||
{
|
||||
fb = framebuffer_crop_int(fb, 0, offset, fb.xres, fb.yres - offset);
|
||||
return fb;
|
||||
}
|
||||
|
||||
struct framebuffer framebuffer_cut_bottom_y(struct framebuffer fb, int offset)
|
||||
{
|
||||
fb = framebuffer_crop_int(fb, 0, 0, fb.xres, fb.yres - offset);
|
||||
return fb;
|
||||
}
|
||||
|
||||
struct framebuffer framebuffer_center_x(struct framebuffer fb, int x, int width)
|
||||
{
|
||||
x = x - width / 2;
|
||||
if ( x < 0 ) { width -= -x; x = 0; }
|
||||
if ( width < 0 ) { width = 0; }
|
||||
fb = framebuffer_crop(fb, x, 0, width, fb.yres);
|
||||
return fb;
|
||||
}
|
||||
|
||||
struct framebuffer framebuffer_center_y(struct framebuffer fb, int y, int height)
|
||||
{
|
||||
y = y - height / 2;
|
||||
if ( y < 0 ) { height -= -y; y = 0; }
|
||||
if ( height < 0 ) { height = 0; }
|
||||
fb = framebuffer_crop(fb, 0, y, fb.xres, height);
|
||||
return fb;
|
||||
}
|
||||
|
||||
struct framebuffer framebuffer_right_x(struct framebuffer fb, int x, int width)
|
||||
{
|
||||
x = x - width;
|
||||
if ( x < 0 ) { width -= -x; x = 0; }
|
||||
if ( width < 0 ) { width = 0; }
|
||||
fb = framebuffer_crop(fb, x, 0, width, fb.yres);
|
||||
return fb;
|
||||
}
|
||||
|
||||
struct framebuffer framebuffer_bottom_y(struct framebuffer fb, int y, int height)
|
||||
{
|
||||
y = y - height;
|
||||
if ( y < 0 ) { height -= -y; y = 0; }
|
||||
if ( height < 0 ) { height = 0; }
|
||||
fb = framebuffer_crop(fb, 0, y, fb.xres, height);
|
||||
return fb;
|
||||
}
|
||||
|
||||
struct framebuffer framebuffer_center_text_x(struct framebuffer fb, int x, const char* str)
|
||||
{
|
||||
int width = (FONT_WIDTH + 1) * strlen(str);
|
||||
return framebuffer_center_x(fb, x, width);
|
||||
}
|
||||
|
||||
struct framebuffer framebuffer_center_text_y(struct framebuffer fb, int y, const char* str)
|
||||
{
|
||||
(void) str;
|
||||
int height = FONT_HEIGHT;
|
||||
return framebuffer_center_y(fb, y, height);
|
||||
}
|
||||
|
||||
struct framebuffer framebuffer_right_text_x(struct framebuffer fb, int x, const char* str)
|
||||
{
|
||||
int width = (FONT_WIDTH + 1) * strlen(str);
|
||||
return framebuffer_right_x(fb, x, width);
|
||||
}
|
||||
|
||||
struct framebuffer framebuffer_bottom_text_y(struct framebuffer fb, int y, const char* str)
|
||||
{
|
||||
(void) str;
|
||||
int height = FONT_HEIGHT;
|
||||
return framebuffer_bottom_y(fb, y, height);
|
||||
}
|
83
login/framebuffer.h
Normal file
83
login/framebuffer.h
Normal file
|
@ -0,0 +1,83 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2014, 2015.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation, either version 3 of the License, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
framebuffer.h
|
||||
Framebuffer utilities.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef FRAMEBUFFER_H
|
||||
#define FRAMEBUFFER_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct framebuffer
|
||||
{
|
||||
size_t pitch;
|
||||
uint32_t* buffer;
|
||||
size_t xres;
|
||||
size_t yres;
|
||||
};
|
||||
|
||||
static inline uint32_t framebuffer_get_pixel(const struct framebuffer fb,
|
||||
size_t x,
|
||||
size_t y)
|
||||
{
|
||||
if ( fb.xres <= x || fb.yres <= y )
|
||||
return 0;
|
||||
return fb.buffer[y * fb.pitch + x];
|
||||
}
|
||||
|
||||
static inline void framebuffer_set_pixel(const struct framebuffer fb,
|
||||
size_t x,
|
||||
size_t y,
|
||||
uint32_t value)
|
||||
{
|
||||
if ( fb.xres <= x || fb.yres <= y )
|
||||
return;
|
||||
fb.buffer[y * fb.pitch + x] = value;
|
||||
}
|
||||
|
||||
struct framebuffer framebuffer_crop(struct framebuffer fb,
|
||||
size_t left,
|
||||
size_t top,
|
||||
size_t width,
|
||||
size_t height);
|
||||
void framebuffer_copy_to_framebuffer(const struct framebuffer dst,
|
||||
const struct framebuffer src);
|
||||
void framebuffer_copy_to_framebuffer_blend(const struct framebuffer dst,
|
||||
const struct framebuffer src);
|
||||
struct framebuffer framebuffer_crop_int(struct framebuffer fb,
|
||||
int left,
|
||||
int top,
|
||||
int width,
|
||||
int height);
|
||||
struct framebuffer framebuffer_cut_left_x(struct framebuffer fb, int offset);
|
||||
struct framebuffer framebuffer_cut_right_x(struct framebuffer fb, int offset);
|
||||
struct framebuffer framebuffer_cut_top_y(struct framebuffer fb, int offset);
|
||||
struct framebuffer framebuffer_cut_bottom_y(struct framebuffer fb, int offset);
|
||||
struct framebuffer framebuffer_center_x(struct framebuffer fb, int x, int width);
|
||||
struct framebuffer framebuffer_center_y(struct framebuffer fb, int y, int height);
|
||||
struct framebuffer framebuffer_right_x(struct framebuffer fb, int x, int width);
|
||||
struct framebuffer framebuffer_bottom_y(struct framebuffer fb, int y, int height);
|
||||
struct framebuffer framebuffer_center_text_x(struct framebuffer fb, int x, const char* str);
|
||||
struct framebuffer framebuffer_center_text_y(struct framebuffer fb, int y, const char* str);
|
||||
struct framebuffer framebuffer_right_text_x(struct framebuffer fb, int x, const char* str);
|
||||
struct framebuffer framebuffer_bottom_text_y(struct framebuffer fb, int y, const char* str);
|
||||
|
||||
#endif
|
845
login/graphical.c
Normal file
845
login/graphical.c
Normal file
|
@ -0,0 +1,845 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2014, 2015.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation, either version 3 of the License, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
graphical.c
|
||||
Graphical login.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include <sys/display.h>
|
||||
#include <sys/display.h>
|
||||
#include <sys/kernelinfo.h>
|
||||
#include <sys/termmode.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <brand.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <poll.h>
|
||||
#include <signal.h>
|
||||
#include <sched.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <termios.h>
|
||||
#include <time.h>
|
||||
#include <timespec.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// TODO: The Sortix <limits.h> doesn't expose this at the moment.
|
||||
#if !defined(HOST_NAME_MAX) && defined(__sortix__)
|
||||
#include <sortix/limits.h>
|
||||
#endif
|
||||
|
||||
#include "framebuffer.h"
|
||||
#include "login.h"
|
||||
#include "pixel.h"
|
||||
#include "vgafont.h"
|
||||
|
||||
#include "arrow.inc"
|
||||
|
||||
enum stage
|
||||
{
|
||||
STAGE_USERNAME,
|
||||
STAGE_PASSWORD,
|
||||
STAGE_CHECKING,
|
||||
};
|
||||
|
||||
struct textbox
|
||||
{
|
||||
char text[256];
|
||||
size_t used;
|
||||
size_t offset;
|
||||
const char* standin;
|
||||
bool password;
|
||||
};
|
||||
|
||||
static uint32_t arrow_buffer[48 * 48];
|
||||
static struct framebuffer arrow_framebuffer = { 48, arrow_buffer, 48, 48 };
|
||||
|
||||
static inline void arrow_initialize()
|
||||
{
|
||||
static bool done = false;
|
||||
if ( done )
|
||||
return;
|
||||
memcpy(arrow_buffer, arrow, sizeof(arrow));
|
||||
done = true;
|
||||
}
|
||||
|
||||
static struct textbox textbox_username;
|
||||
static struct textbox textbox_password;
|
||||
|
||||
struct glogin
|
||||
{
|
||||
struct check chk;
|
||||
int fd_mouse;
|
||||
struct dispmsg_crtc_mode mode;
|
||||
struct framebuffer fade_from_fb;
|
||||
struct timespec fade_from_begin;
|
||||
struct timespec fade_from_end;
|
||||
bool fading_from;
|
||||
uint32_t* last_fb_buffer;
|
||||
size_t last_fb_buffer_size;
|
||||
int pointer_x;
|
||||
int pointer_y;
|
||||
size_t mouse_byte_count;
|
||||
uint8_t mouse_bytes[3];
|
||||
enum stage stage;
|
||||
bool animating;
|
||||
const char* warning;
|
||||
bool pointer_working;
|
||||
};
|
||||
|
||||
static struct glogin state;
|
||||
|
||||
static bool get_graphical_mode(struct dispmsg_crtc_mode* mode)
|
||||
{
|
||||
struct dispmsg_get_crtc_mode msg;
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
msg.msgid = DISPMSG_GET_CRTC_MODE;
|
||||
msg.device = 0; // TODO: Multi-screen support!
|
||||
msg.connector = 0; // TODO: Multi-screen support!
|
||||
if ( dispmsg_issue(&msg, sizeof(msg)) != 0 )
|
||||
{
|
||||
warn("dispmsg_issue: DISPMSG_GET_CRTC_MODE");
|
||||
return false;
|
||||
}
|
||||
*mode = msg.mode;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool is_graphical_mode(struct dispmsg_crtc_mode* mode)
|
||||
{
|
||||
return (mode->control & DISPMSG_CONTROL_VALID) &&
|
||||
!(mode->control & DISPMSG_CONTROL_VGA) &&
|
||||
mode->fb_format == 32;
|
||||
}
|
||||
|
||||
static void textbox_initialize(struct textbox* textbox, const char* standin)
|
||||
{
|
||||
memset(textbox, 0, sizeof(*textbox));
|
||||
textbox->standin = standin;
|
||||
}
|
||||
|
||||
static void textbox_reset(struct textbox* textbox)
|
||||
{
|
||||
explicit_bzero(textbox->text, sizeof(textbox->text));
|
||||
textbox->used = 0;
|
||||
textbox->offset = 0;
|
||||
}
|
||||
|
||||
static void textbox_type_char(struct textbox* textbox, char c)
|
||||
{
|
||||
if ( textbox->used + 1 == sizeof(textbox->text) )
|
||||
return;
|
||||
memmove(textbox->text + textbox->offset + 1,
|
||||
textbox->text + textbox->offset,
|
||||
textbox->used - textbox->offset + 1);
|
||||
textbox->text[textbox->offset++] = c;
|
||||
textbox->used++;
|
||||
}
|
||||
|
||||
static void textbox_type_backspace(struct textbox* textbox)
|
||||
{
|
||||
if ( textbox->offset == 0 )
|
||||
return;
|
||||
memmove(textbox->text + textbox->offset - 1,
|
||||
textbox->text + textbox->offset,
|
||||
textbox->used - textbox->offset + 1);
|
||||
textbox->offset--;
|
||||
textbox->used--;
|
||||
}
|
||||
|
||||
void render_right_text(struct framebuffer fb, const char* str, uint32_t color)
|
||||
{
|
||||
size_t len = strlen(str);
|
||||
for ( size_t i = 0; i < len; i++ )
|
||||
{
|
||||
int x = fb.xres - ((int) FONT_WIDTH+1) * ((int) len - (int) i);
|
||||
render_char(framebuffer_crop(fb, x, 0, fb.xres, fb.yres), str[i], color);
|
||||
}
|
||||
}
|
||||
|
||||
void render_right_text_if_needed(struct framebuffer fb, const char* str, uint32_t color)
|
||||
{
|
||||
size_t len = strlen(str);
|
||||
size_t shown_len = fb.xres / (FONT_WIDTH+1);
|
||||
if ( len <= shown_len )
|
||||
render_text(fb, str, color);
|
||||
else
|
||||
render_right_text(fb, str, color);
|
||||
}
|
||||
|
||||
static void render_background(struct framebuffer fb)
|
||||
{
|
||||
uint32_t bg_color = make_color(0x89 * 2/3, 0xc7 * 2/3, 0xff * 2/3);
|
||||
for ( size_t y = 0; y < fb.yres; y++ )
|
||||
for ( size_t x = 0; x < fb.xres; x++ )
|
||||
framebuffer_set_pixel(fb, x, y, bg_color);
|
||||
}
|
||||
|
||||
static void render_pointer(struct framebuffer fb)
|
||||
{
|
||||
int p_hwidth = arrow_framebuffer.xres / 2;
|
||||
int p_hheight = arrow_framebuffer.yres / 2;
|
||||
int p_x = state.pointer_x - p_hwidth;
|
||||
int p_y = state.pointer_y - p_hheight;
|
||||
struct framebuffer arrow_render = arrow_framebuffer;
|
||||
if ( p_x < 0 )
|
||||
{
|
||||
arrow_render = framebuffer_crop(arrow_render, -p_x, 0, arrow_render.xres, arrow_render.yres);
|
||||
p_x = 0;
|
||||
}
|
||||
if ( p_y < 0 )
|
||||
{
|
||||
arrow_render = framebuffer_crop(arrow_render, 0, -p_y, arrow_render.xres, arrow_render.yres);
|
||||
p_y = 0;
|
||||
}
|
||||
struct framebuffer fb_dst = framebuffer_crop(fb, p_x, p_y, fb.xres, fb.yres);
|
||||
framebuffer_copy_to_framebuffer_blend(fb_dst, arrow_render);
|
||||
}
|
||||
|
||||
static char* brand_line()
|
||||
{
|
||||
char version[64];
|
||||
version[0] = '\0';
|
||||
kernelinfo("version", version, sizeof(version));
|
||||
char* result = NULL;
|
||||
asprintf(&result, "%s %s - %s",
|
||||
BRAND_OPERATING_SYSTEM_NAME,
|
||||
version,
|
||||
BRAND_DISTRIBUTION_WEBSITE);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void render_information(struct framebuffer fb)
|
||||
{
|
||||
struct framebuffer textfb;
|
||||
|
||||
char* brandstr = brand_line();
|
||||
if ( brandstr )
|
||||
{
|
||||
textfb = fb;
|
||||
textfb = framebuffer_center_text_x(textfb, fb.xres/2, brandstr);
|
||||
textfb = framebuffer_bottom_text_y(textfb, fb.yres, brandstr);
|
||||
render_text(textfb, brandstr, make_color(255, 255, 255));
|
||||
free(brandstr);
|
||||
}
|
||||
}
|
||||
|
||||
static void render_textbox(struct framebuffer fb, struct textbox* textbox)
|
||||
{
|
||||
for ( int y = 0; y < (int) fb.yres; y++ )
|
||||
{
|
||||
for ( int x = 0; x < (int) fb.xres; x++ )
|
||||
{
|
||||
uint32_t color;
|
||||
if ( x == 0 || x == (int) fb.xres - 1 ||
|
||||
y == 0 || y == (int) fb.yres - 1 )
|
||||
color = make_color(32, 32, 32);
|
||||
else
|
||||
color = make_color(255, 255, 255);
|
||||
framebuffer_set_pixel(fb, x, y, color);
|
||||
}
|
||||
}
|
||||
|
||||
fb = framebuffer_cut_left_x(fb, 6);
|
||||
fb = framebuffer_cut_right_x(fb, 6);
|
||||
fb = framebuffer_cut_top_y(fb, 6);
|
||||
fb = framebuffer_cut_bottom_y(fb, 6);
|
||||
if ( !textbox->used )
|
||||
render_right_text_if_needed(fb, textbox->standin, make_color(160, 160, 160));
|
||||
else if ( textbox->password )
|
||||
{
|
||||
int x = 0;
|
||||
while ( x + (FONT_WIDTH+1) <= (int) fb.xres )
|
||||
{
|
||||
render_char(framebuffer_crop(fb, x, 0, fb.xres, fb.yres), '*',
|
||||
make_color(200, 200, 200));
|
||||
x += (FONT_WIDTH+1);
|
||||
}
|
||||
}
|
||||
else
|
||||
render_right_text_if_needed(fb, textbox->text, make_color(0, 0, 0));
|
||||
}
|
||||
|
||||
static void render_form(struct framebuffer fb)
|
||||
{
|
||||
int typearea_width = (FONT_WIDTH + 1) * 25;
|
||||
int typearea_height = FONT_HEIGHT;
|
||||
int textbox_margin = 6;
|
||||
int textbox_width = typearea_width + 2 * textbox_margin;
|
||||
int textbox_height = typearea_height + 2 * textbox_margin;
|
||||
int form_margin = 10;
|
||||
int form_width = textbox_width + 2 * form_margin;
|
||||
int form_height = textbox_height + 2 * form_margin;
|
||||
int BORDER_WIDTH = 8;
|
||||
int TITLE_HEIGHT = 28;
|
||||
int b0 = 0;
|
||||
int b1 = 1;
|
||||
int b2 = 2;
|
||||
int b3 = BORDER_WIDTH;
|
||||
int t0 = TITLE_HEIGHT;
|
||||
int window_width = BORDER_WIDTH + form_width + BORDER_WIDTH;
|
||||
int window_height = TITLE_HEIGHT + form_height + BORDER_WIDTH;
|
||||
|
||||
if ( state.warning )
|
||||
{
|
||||
struct framebuffer warnfb = fb;
|
||||
int y = (fb.yres - 50 - window_height) / 2 - 2 * FONT_HEIGHT;
|
||||
warnfb = framebuffer_cut_top_y(warnfb, y);
|
||||
int w = strlen(state.warning) * (FONT_WIDTH+1);
|
||||
warnfb = framebuffer_center_x(warnfb, fb.xres / 2, w);
|
||||
render_text(warnfb, state.warning, make_color(255, 0, 0));
|
||||
}
|
||||
|
||||
fb = framebuffer_center_x(fb, fb.xres / 2, window_width);
|
||||
fb = framebuffer_center_y(fb, (fb.yres - 50) / 2, window_height);
|
||||
|
||||
uint32_t glass_color = make_color_a(200, 200, 255, 192);
|
||||
uint32_t title_color = make_color_a(16, 16, 16, 240);
|
||||
|
||||
for ( int y = 0; y < (int) fb.yres; y++ )
|
||||
{
|
||||
for ( int x = 0; x < (int) fb.xres; x++ )
|
||||
{
|
||||
uint32_t color;
|
||||
if ( x == b0 || x == (int) fb.xres - (b0+1) ||
|
||||
y == b0 || y == (int) fb.yres - (b0+1) )
|
||||
color = make_color_a(0, 0, 0, 32);
|
||||
else if ( x == b1 || x == (int) fb.xres - (b1+1) ||
|
||||
y == b1 || y == (int) fb.yres - (b1+1) )
|
||||
color = make_color_a(0, 0, 0, 64);
|
||||
else if ( x == b2 || x == (int) fb.xres - (b2+1) ||
|
||||
y == b2 || y == (int) fb.yres - (b2+1) )
|
||||
color = make_color(240, 240, 250);
|
||||
else if ( x < (b3-1) || x > (int) fb.xres - (b3+1-1) ||
|
||||
y < (t0-1) || y > (int) fb.yres - (b3+1-1) )
|
||||
color = glass_color;
|
||||
else if ( x == (b3-1) || x == (int) fb.xres - (b3+1-1) ||
|
||||
y == (t0-1) || y == (int) fb.yres - (b3+1-1) )
|
||||
color = make_color(64, 64, 64);
|
||||
else
|
||||
continue;
|
||||
uint32_t bg = framebuffer_get_pixel(fb, x, y);
|
||||
framebuffer_set_pixel(fb, x, y, blend_pixel(bg, color));
|
||||
}
|
||||
}
|
||||
|
||||
fb = framebuffer_cut_left_x(fb, BORDER_WIDTH);
|
||||
fb = framebuffer_cut_right_x(fb, BORDER_WIDTH);
|
||||
|
||||
char hostname[HOST_NAME_MAX + 1];
|
||||
hostname[0] = '\0';
|
||||
gethostname(hostname, sizeof(hostname));
|
||||
const char* tt = hostname;
|
||||
size_t tt_length = strlen(tt);
|
||||
size_t tt_max_width = fb.xres;
|
||||
size_t tt_desired_width = tt_length * (FONT_WIDTH+1);
|
||||
size_t tt_width = tt_desired_width < tt_max_width ? tt_desired_width : tt_max_width;
|
||||
size_t tt_height = FONT_HEIGHT;
|
||||
size_t tt_pos_x = BORDER_WIDTH + (tt_max_width - tt_width) / 2;
|
||||
size_t tt_pos_y = (TITLE_HEIGHT - FONT_HEIGHT) / 2 + 2;
|
||||
uint32_t tt_color = title_color;
|
||||
render_text(framebuffer_crop(fb, tt_pos_x, tt_pos_y, tt_width, tt_height), tt, tt_color);
|
||||
|
||||
fb = framebuffer_cut_top_y(fb, TITLE_HEIGHT);
|
||||
fb = framebuffer_cut_bottom_y(fb, BORDER_WIDTH);
|
||||
|
||||
for ( int y = 0; y < (int) fb.yres; y++ )
|
||||
{
|
||||
for ( int x = 0; x < (int) fb.xres; x++ )
|
||||
{
|
||||
framebuffer_set_pixel(fb, x, y, make_color(214, 214, 214));
|
||||
}
|
||||
}
|
||||
|
||||
struct framebuffer boxfb = fb;
|
||||
boxfb = framebuffer_cut_left_x(boxfb, form_margin);
|
||||
boxfb = framebuffer_cut_right_x(boxfb, form_margin);
|
||||
boxfb = framebuffer_cut_top_y(boxfb, form_margin);
|
||||
boxfb = framebuffer_cut_bottom_y(boxfb, form_margin);
|
||||
switch ( state.stage )
|
||||
{
|
||||
case STAGE_USERNAME: render_textbox(boxfb, &textbox_username); break;
|
||||
case STAGE_PASSWORD: render_textbox(boxfb, &textbox_password); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
static void render_progress(struct framebuffer fb)
|
||||
{
|
||||
state.animating = true;
|
||||
struct timespec now;
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
float time = (float) now.tv_sec + (float) now.tv_nsec * 10E-9;
|
||||
float rotslow_cos = cos(-time / 30.0f * M_PI * 2.0);
|
||||
float rotslow_sin = sin(-time / 30.0f * M_PI * 2.0);
|
||||
int size = 32;
|
||||
int width = 4;
|
||||
float widthf = ((float) width / (float) size) * 2.0f;
|
||||
float innersq = (1.0f - widthf) * (1.0f - widthf);
|
||||
float outersq = (1.0f) * (1.0f);
|
||||
fb = framebuffer_center_x(fb, fb.xres / 2, size);
|
||||
fb = framebuffer_center_y(fb, (fb.yres - 50) / 2, size);
|
||||
for ( size_t y = 0; y < fb.yres; y++ )
|
||||
{
|
||||
float yfi = ((float) y / (float) size) * 2.0f - 1.0f;
|
||||
for ( size_t x = 0; x < fb.xres; x++ )
|
||||
{
|
||||
float xfi = ((float) x / (float) size) * 2.0f - 1.0f;
|
||||
float distsq = xfi * xfi + yfi * yfi;
|
||||
if ( distsq < innersq )
|
||||
continue;
|
||||
if ( distsq > outersq )
|
||||
continue;
|
||||
float af = fabs((distsq - innersq) / (outersq - innersq) * 2.0f - 1.0f);
|
||||
af = 1.0 - af * af;
|
||||
uint8_t a = (uint8_t) (af * 255.0f);
|
||||
float xf = xfi;
|
||||
float yf = yfi;
|
||||
xf = rotslow_cos * xf + rotslow_sin * yf;
|
||||
yf = -rotslow_sin * xf + rotslow_cos * yf;
|
||||
if ( -widthf < yf && yf < widthf )
|
||||
continue;
|
||||
uint8_t r = 0;
|
||||
uint8_t g = 127.5 + 127.5 * xf;
|
||||
uint8_t b = 255;
|
||||
uint32_t bg = framebuffer_get_pixel(fb, x, y);
|
||||
uint32_t fg = make_color_a(r, g, b, a);
|
||||
framebuffer_set_pixel(fb, x, y, blend_pixel(bg, fg));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void render_login(struct framebuffer fb)
|
||||
{
|
||||
render_background(fb);
|
||||
if ( false )
|
||||
render_information(fb);
|
||||
switch ( state.stage )
|
||||
{
|
||||
case STAGE_USERNAME: render_form(fb); break;
|
||||
case STAGE_PASSWORD: render_form(fb); break;
|
||||
case STAGE_CHECKING: render_progress(fb); break;
|
||||
}
|
||||
if ( state.pointer_working )
|
||||
render_pointer(fb);
|
||||
}
|
||||
|
||||
static void glogin_fade_from_end(struct glogin* state)
|
||||
{
|
||||
state->fading_from = false;
|
||||
free(state->fade_from_fb.buffer);
|
||||
}
|
||||
|
||||
static uint32_t* glogin_malloc_fb_buffer(struct glogin* state,
|
||||
size_t size)
|
||||
{
|
||||
if ( state->last_fb_buffer )
|
||||
{
|
||||
if ( state->last_fb_buffer_size == size )
|
||||
{
|
||||
uint32_t* result = state->last_fb_buffer;
|
||||
state->last_fb_buffer = NULL;
|
||||
return result;
|
||||
}
|
||||
free(state->last_fb_buffer);
|
||||
state->last_fb_buffer = NULL;
|
||||
}
|
||||
uint32_t* result = (uint32_t*) malloc(size);
|
||||
if ( !result )
|
||||
{
|
||||
glogin_fade_from_end(state);
|
||||
result = (uint32_t*) malloc(size);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void glogin_free_fb_buffer(struct glogin* state,
|
||||
uint32_t* buffer,
|
||||
size_t size)
|
||||
{
|
||||
if ( state->last_fb_buffer )
|
||||
free(state->last_fb_buffer);
|
||||
state->last_fb_buffer = buffer;
|
||||
state->last_fb_buffer_size = size;
|
||||
}
|
||||
|
||||
static bool screen_capture(struct glogin* state, struct framebuffer* fb)
|
||||
{
|
||||
fb->xres = state->mode.view_xres;
|
||||
fb->yres = state->mode.view_yres;
|
||||
fb->pitch = state->mode.view_xres;
|
||||
size_t size = sizeof(uint32_t) * fb->xres * fb->yres;
|
||||
fb->buffer = (uint32_t*) glogin_malloc_fb_buffer(state, size);
|
||||
if ( !fb->buffer )
|
||||
return false;
|
||||
struct dispmsg_write_memory msg;
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
msg.msgid = DISPMSG_READ_MEMORY;
|
||||
msg.device = 0; // TODO: Multi-screen support!
|
||||
msg.offset = 0; // TODO: mode.fb_location!
|
||||
msg.size = fb->xres * fb->yres * sizeof(fb->buffer[0]);
|
||||
msg.src = (uint8_t*) fb->buffer;
|
||||
if ( dispmsg_issue(&msg, sizeof(msg)) != 0 )
|
||||
{
|
||||
warn("dispmsg_issue: DISPMSG_READ_MEMORY");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool begin_render(struct glogin* state, struct framebuffer* fb)
|
||||
{
|
||||
if ( !get_graphical_mode(&state->mode) )
|
||||
return false;
|
||||
fb->xres = state->mode.view_xres;
|
||||
fb->yres = state->mode.view_yres;
|
||||
fb->pitch = state->mode.view_xres;
|
||||
size_t size = sizeof(uint32_t) * fb->xres * fb->yres;
|
||||
fb->buffer = (uint32_t*) glogin_malloc_fb_buffer(state, size);
|
||||
if ( !fb->buffer )
|
||||
{
|
||||
warn("malloc");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool finish_render(struct glogin* state, struct framebuffer* fb)
|
||||
{
|
||||
struct dispmsg_write_memory msg;
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
msg.msgid = DISPMSG_WRITE_MEMORY;
|
||||
msg.device = 0; // TODO: Multi-screen support!
|
||||
msg.offset = 0; // TODO: mode.fb_location!
|
||||
msg.size = sizeof(uint32_t) * fb->xres * fb->yres;
|
||||
msg.src = (uint8_t*) fb->buffer;
|
||||
if ( dispmsg_issue(&msg, sizeof(msg)) != 0 )
|
||||
{
|
||||
warn("dispmsg_issue: DISPMSG_WRITE_MEMORY");
|
||||
free(fb->buffer);
|
||||
return false;
|
||||
}
|
||||
glogin_free_fb_buffer(state, fb->buffer, msg.size);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool render(struct glogin* state)
|
||||
{
|
||||
state->animating = false;
|
||||
struct framebuffer fb;
|
||||
if ( !begin_render(state, &fb) )
|
||||
return false;
|
||||
render_login(fb);
|
||||
struct timespec now;
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
if ( state->fading_from && timespec_lt(now, state->fade_from_end) )
|
||||
{
|
||||
struct timespec duration_ts =
|
||||
timespec_sub(state->fade_from_end, state->fade_from_begin);
|
||||
struct timespec elapsed_ts = timespec_sub(now, state->fade_from_begin);
|
||||
float duration = (float) duration_ts.tv_sec + (float) duration_ts.tv_nsec * 10E-9;
|
||||
float elapsed = (float) elapsed_ts.tv_sec + (float) elapsed_ts.tv_nsec * 10E-9;
|
||||
float fade_from_alpha_f = 255.0 * elapsed / duration;
|
||||
if ( fade_from_alpha_f < 0.0f ) fade_from_alpha_f = 0.0f;
|
||||
if ( fade_from_alpha_f > 255.0f ) fade_from_alpha_f = 255.0f;
|
||||
uint8_t fade_from_alpha = (uint8_t) fade_from_alpha_f;
|
||||
uint32_t and_mask = ~make_color(0, 0, 0);
|
||||
uint32_t or_mask = make_color_a(0, 0, 0, 255 - fade_from_alpha);
|
||||
for ( int y = 0; y < (int) state->fade_from_fb.yres; y++ )
|
||||
{
|
||||
for ( int x = 0; x < (int) state->fade_from_fb.xres; x++ )
|
||||
{
|
||||
uint32_t color = framebuffer_get_pixel(state->fade_from_fb, x, y);
|
||||
color = (color & and_mask) | or_mask;
|
||||
framebuffer_set_pixel(state->fade_from_fb, x, y, color);
|
||||
}
|
||||
}
|
||||
framebuffer_copy_to_framebuffer_blend(fb, state->fade_from_fb);
|
||||
state->animating = true;
|
||||
}
|
||||
|
||||
else if ( state->fading_from )
|
||||
glogin_fade_from_end(state);
|
||||
if ( !finish_render(state, &fb) )
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void think(struct glogin* state)
|
||||
{
|
||||
if ( state->stage == STAGE_CHECKING )
|
||||
{
|
||||
bool result;
|
||||
if ( !check_end(&state->chk, &result, true) )
|
||||
{
|
||||
sched_yield();
|
||||
return;
|
||||
}
|
||||
if ( result )
|
||||
{
|
||||
if ( !login(textbox_username.text) )
|
||||
state->warning = strerror(errno);
|
||||
state->stage = STAGE_USERNAME;
|
||||
textbox_reset(&textbox_username);
|
||||
}
|
||||
else
|
||||
{
|
||||
state->stage = STAGE_USERNAME;
|
||||
textbox_reset(&textbox_username);
|
||||
if ( errno == EACCES )
|
||||
state->warning = "Invalid username/password";
|
||||
else
|
||||
state->warning = strerror(errno);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void keyboard_event(struct glogin* state, uint32_t codepoint)
|
||||
{
|
||||
if ( codepoint == '\n' )
|
||||
{
|
||||
state->warning = NULL;
|
||||
switch ( state->stage )
|
||||
{
|
||||
case STAGE_USERNAME:
|
||||
if ( !strcmp(textbox_username.text, "exit") )
|
||||
exit(0);
|
||||
if ( !strcmp(textbox_username.text, "poweroff") )
|
||||
exit(0);
|
||||
if ( !strcmp(textbox_username.text, "reboot") )
|
||||
exit(1);
|
||||
state->stage = STAGE_PASSWORD;
|
||||
textbox_reset(&textbox_password);
|
||||
break;
|
||||
case STAGE_PASSWORD:
|
||||
state->stage = STAGE_CHECKING;
|
||||
if ( !check_begin(&state->chk, textbox_username.text,
|
||||
textbox_password.text, true) )
|
||||
{
|
||||
state->stage = STAGE_USERNAME;
|
||||
state->warning = strerror(errno);
|
||||
}
|
||||
break;
|
||||
case STAGE_CHECKING:
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
struct textbox* textbox = NULL;
|
||||
switch ( state->stage )
|
||||
{
|
||||
case STAGE_USERNAME: textbox = &textbox_username; break;
|
||||
case STAGE_PASSWORD: textbox = &textbox_password; break;
|
||||
case STAGE_CHECKING: break;
|
||||
}
|
||||
if ( textbox && codepoint < 128 )
|
||||
{
|
||||
if ( codepoint == '\b' || codepoint == 127 )
|
||||
textbox_type_backspace(textbox);
|
||||
else
|
||||
textbox_type_char(textbox, (char) codepoint);
|
||||
}
|
||||
}
|
||||
|
||||
static void mouse_event(struct glogin* state, unsigned char byte)
|
||||
{
|
||||
state->pointer_working = true;
|
||||
if ( state->mouse_byte_count == 0 && !(byte & 1 << 3) )
|
||||
return;
|
||||
if ( state->mouse_byte_count < 3 )
|
||||
state->mouse_bytes[state->mouse_byte_count++] = byte;
|
||||
if ( state->mouse_byte_count < 3 )
|
||||
return;
|
||||
state->mouse_byte_count = 0;
|
||||
unsigned char* bytes = state->mouse_bytes;
|
||||
int xm = bytes[1];
|
||||
int ym = bytes[2];
|
||||
if ( xm && bytes[0] & (1 << 4) )
|
||||
xm = xm - 256;
|
||||
if ( ym && bytes[0] & (1 << 5) )
|
||||
ym = ym - 256;
|
||||
if ( (bytes[0] & (1 << 6)) || (bytes[0] & (1 << 7)) )
|
||||
{
|
||||
xm = 0;
|
||||
ym = 0;
|
||||
}
|
||||
ym = -ym;
|
||||
int old_pointer_x = state->pointer_x;
|
||||
int old_pointer_y = state->pointer_y;
|
||||
if ( xm*xm + ym*ym >= 2*2 )
|
||||
{
|
||||
xm *= 2;
|
||||
ym *= 2;
|
||||
}
|
||||
else if ( xm*xm + ym*ym >= 5*5 )
|
||||
{
|
||||
xm *= 3;
|
||||
ym *= 3;
|
||||
}
|
||||
state->pointer_x += xm;
|
||||
state->pointer_y += ym;
|
||||
if ( state->pointer_x < 0 )
|
||||
state->pointer_x = 0;
|
||||
if ( state->pointer_y < 0 )
|
||||
state->pointer_y = 0;
|
||||
if ( state->mode.view_xres <= (size_t) state->pointer_x )
|
||||
state->pointer_x = state->mode.view_xres;
|
||||
if ( state->mode.view_yres <= (size_t) state->pointer_y )
|
||||
state->pointer_y = state->mode.view_yres;
|
||||
xm = state->pointer_x - old_pointer_x;
|
||||
ym = state->pointer_y - old_pointer_y;
|
||||
if ( (bytes[0] & 1 << 0) )
|
||||
{
|
||||
(void) xm;
|
||||
(void) ym;
|
||||
}
|
||||
}
|
||||
|
||||
void glogin_destroy(struct glogin* state)
|
||||
{
|
||||
if ( 0 <= state->fd_mouse )
|
||||
close(state->fd_mouse);
|
||||
if ( state->fading_from )
|
||||
free(state->fade_from_fb.buffer);
|
||||
}
|
||||
|
||||
bool glogin_init(struct glogin* state)
|
||||
{
|
||||
memset(state, 0, sizeof(*state));
|
||||
state->fd_mouse = -1;
|
||||
|
||||
if ( !get_graphical_mode(&state->mode) )
|
||||
{
|
||||
warn("dispmsg_issue");
|
||||
glogin_destroy(state);
|
||||
return false;
|
||||
}
|
||||
if ( !is_graphical_mode(&state->mode) ||
|
||||
state->mode.view_xres < 128 ||
|
||||
state->mode.view_yres < 128 )
|
||||
{
|
||||
glogin_destroy(state);
|
||||
return false;
|
||||
}
|
||||
if ( !load_font() )
|
||||
{
|
||||
warn("/dev/vgafont");
|
||||
glogin_destroy(state);
|
||||
return false;
|
||||
}
|
||||
state->fd_mouse = open("/dev/mouse", O_RDONLY | O_CLOEXEC);
|
||||
if ( settermmode(0, TERMMODE_KBKEY | TERMMODE_UNICODE | TERMMODE_NONBLOCK) < 0 )
|
||||
{
|
||||
warn("settermmode");
|
||||
return false;
|
||||
}
|
||||
fsync(0);
|
||||
arrow_initialize();
|
||||
textbox_initialize(&textbox_username, "Username");
|
||||
textbox_initialize(&textbox_password, "Password");
|
||||
textbox_password.password = true;
|
||||
state->pointer_x = state->mode.view_xres / 2;
|
||||
state->pointer_y = state->mode.view_yres / 2;
|
||||
if ( screen_capture(state, &state->fade_from_fb) )
|
||||
{
|
||||
state->fading_from = true;
|
||||
clock_gettime(CLOCK_MONOTONIC, &state->fade_from_begin);
|
||||
struct timespec duration = timespec_make(0, 150*1000*1000);
|
||||
state->fade_from_end = timespec_add(state->fade_from_begin, duration);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int glogin_main(struct glogin* state)
|
||||
{
|
||||
while ( true )
|
||||
{
|
||||
think(state);
|
||||
if ( !render(state) )
|
||||
break;
|
||||
struct pollfd pfds[2];
|
||||
memset(pfds, 0, sizeof(pfds));
|
||||
pfds[0].fd = -1;
|
||||
pfds[1].fd = -1;
|
||||
if ( state->stage != STAGE_CHECKING )
|
||||
{
|
||||
pfds[0].fd = 0;
|
||||
pfds[0].events = POLLIN;
|
||||
pfds[0].revents = 0;
|
||||
}
|
||||
if ( 0 <= state->fd_mouse )
|
||||
{
|
||||
pfds[1].fd = state->fd_mouse;
|
||||
pfds[1].events = POLLIN;
|
||||
pfds[1].revents = 0;
|
||||
}
|
||||
nfds_t nfds = 2;
|
||||
struct timespec wake_now_ts = timespec_make(0, 0);
|
||||
struct timespec* wake = state->animating ? &wake_now_ts : NULL;
|
||||
int num_events = ppoll(pfds, nfds, wake, NULL);
|
||||
if ( num_events < 0 )
|
||||
{
|
||||
warn("poll");
|
||||
break;
|
||||
}
|
||||
for ( nfds_t i = 0; i < nfds; i++ )
|
||||
{
|
||||
if ( pfds[i].fd == -1 )
|
||||
continue;
|
||||
if ( (pfds[i].revents & POLLERR) ||
|
||||
(pfds[i].revents & POLLHUP) ||
|
||||
(pfds[i].revents & POLLNVAL) )
|
||||
{
|
||||
warnx("poll failure on %s", i == 0 ? "keyboard" : "mouse");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( pfds[0].fd != -1 && pfds[0].revents )
|
||||
{
|
||||
uint32_t codepoint;
|
||||
while ( read(0, &codepoint, sizeof(codepoint)) == sizeof(codepoint) )
|
||||
keyboard_event(state, codepoint);
|
||||
}
|
||||
if ( pfds[1].fd != -1 && pfds[1].revents )
|
||||
{
|
||||
unsigned char events[64];
|
||||
ssize_t amount = read(state->fd_mouse, events, sizeof(events));
|
||||
for ( ssize_t i = 0; i < amount; i++ )
|
||||
mouse_event(state, events[i]);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int graphical(void)
|
||||
{
|
||||
if ( access("/etc/login.conf.textual", F_OK) == 0 )
|
||||
return -1;
|
||||
if ( !glogin_init(&state) )
|
||||
return -1;
|
||||
int result = glogin_main(&state);
|
||||
glogin_destroy(&state);
|
||||
return result;
|
||||
}
|
74
login/login.8
Normal file
74
login/login.8
Normal file
|
@ -0,0 +1,74 @@
|
|||
.Dd $Mdocdate: October 6 2015 $
|
||||
.Dt LOGIN 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm login
|
||||
.Nd authenticate users and run personal session
|
||||
.Sh SYNOPSIS
|
||||
.Nm login
|
||||
.Sh DESCRIPTION
|
||||
.Nm login
|
||||
interactively authenticates users by asking them to enter their username and
|
||||
password. The passwords are checked against the password hashes in
|
||||
.Pa /etc/passwd
|
||||
as described in
|
||||
.Xr passwd 5 .
|
||||
.Nm login
|
||||
creates a session as the requested user upon successful authentication.
|
||||
.Pp
|
||||
.Nm login
|
||||
has a graphical interface if the display is graphical and uses a textual
|
||||
interface otherwise. The textual interface is forced if
|
||||
.Pa /etc/login.conf.textual
|
||||
exists. The process remains running in the background and takes
|
||||
over again when the user session exits.
|
||||
.Pp
|
||||
Type a special username to perform special options:
|
||||
.Pp
|
||||
.Bl -tag -width "poweroff" -compact -offset indent
|
||||
.It exit
|
||||
alias for poweroff
|
||||
.It poweroff
|
||||
exit asking for powering off the computer
|
||||
.It reboot
|
||||
exit asking for rebooting the computer
|
||||
.El
|
||||
.Sh SECURITY
|
||||
There is currently no method to confirm the login screen is in fact real other
|
||||
than witnessing a pristine boot. Local users can log in and show a counterfeit
|
||||
login screen that look and behave like the real
|
||||
.Nm login
|
||||
program and trick the next user into revealing their password.
|
||||
.Sh ENVIRONMENT
|
||||
.Nm login
|
||||
sets the following environment variables to match the authenticated user:
|
||||
.Bl -tag -width "LOGNAME"
|
||||
.It Ev HOME
|
||||
home directory
|
||||
.It Ev LOGNAME
|
||||
username
|
||||
.It Ev SHELL
|
||||
shell
|
||||
.It Ev USER
|
||||
username
|
||||
.El
|
||||
.Sh FILES
|
||||
.Bl -tag -width "/etc/passwd" -compact
|
||||
.It Pa /etc/passwd
|
||||
user database (see
|
||||
.Xr passwd 5 )
|
||||
.It Pa /etc/login.conf.textual
|
||||
textual interface is forced if this file exists
|
||||
.El
|
||||
.Sh EXIT STATUS
|
||||
.Nm login
|
||||
exits 0 if the computer should power off, exits 1 if the computer should
|
||||
reboot, or exits 2 on fatal failure and the boot should halt.
|
||||
.Sh SEE ALSO
|
||||
.Xr crypt_checkpass 3 ,
|
||||
.Xr passwd 5 ,
|
||||
.Xr init 8 ,
|
||||
.Xr login 8
|
||||
.Sh BUGS
|
||||
.Nm login
|
||||
only supports a single monitor. The mouse code is less than perfect.
|
438
login/login.c
Normal file
438
login/login.c
Normal file
|
@ -0,0 +1,438 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2014, 2015.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation, either version 3 of the License, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
login.c
|
||||
Authenticates users.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include <sys/termmode.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <ioleast.h>
|
||||
#include <limits.h>
|
||||
#include <locale.h>
|
||||
#include <pwd.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// TODO: The Sortix <limits.h> doesn't expose this at the moment.
|
||||
#if !defined(HOST_NAME_MAX) && defined(__sortix__)
|
||||
#include <sortix/limits.h>
|
||||
#endif
|
||||
|
||||
#include "login.h"
|
||||
|
||||
static void on_interrupt_signal(int signum)
|
||||
{
|
||||
if ( signum == SIGINT )
|
||||
dprintf(1, "^C");
|
||||
if ( signum == SIGQUIT )
|
||||
dprintf(1, "^\\");
|
||||
}
|
||||
|
||||
bool check_real(const char* username, const char* password)
|
||||
{
|
||||
char fakehashbuf[128];
|
||||
char goodhashbuf[128];
|
||||
size_t fakematch = 0;
|
||||
size_t goodmatch = 0;
|
||||
const char* fakehash = NULL;
|
||||
const char* goodhash = NULL;
|
||||
setpwent();
|
||||
struct passwd* pwd;
|
||||
while ( (errno = 0, pwd = getpwent()) )
|
||||
{
|
||||
if ( !strcmp(username, pwd->pw_name) )
|
||||
{
|
||||
strlcpy(goodhashbuf, pwd->pw_passwd, sizeof(goodhashbuf));
|
||||
goodhash = goodhashbuf;
|
||||
goodmatch++;
|
||||
}
|
||||
else
|
||||
{
|
||||
strlcpy(fakehashbuf, pwd->pw_passwd, sizeof(fakehashbuf));
|
||||
fakehash = fakehashbuf;
|
||||
fakematch++;
|
||||
}
|
||||
}
|
||||
int errnum = errno;
|
||||
endpwent();
|
||||
if ( errnum != 0 )
|
||||
return errno = errnum, false;
|
||||
if ( 1 < goodmatch )
|
||||
return errno = EACCES, false;
|
||||
errno = 0;
|
||||
(void) fakehash;
|
||||
return crypt_checkpass(password, goodhash) == 0;
|
||||
}
|
||||
|
||||
bool check_begin(struct check* chk,
|
||||
const char* username,
|
||||
const char* password,
|
||||
bool restrict_termmode)
|
||||
{
|
||||
memset(chk, 0, sizeof(*chk));
|
||||
if ( tcgetattr(0, &chk->tio) )
|
||||
return false;
|
||||
int pipe_fds[2];
|
||||
if ( pipe2(pipe_fds, O_CLOEXEC) < 0 )
|
||||
return false;
|
||||
sigset_t sigttou;
|
||||
sigemptyset(&sigttou);
|
||||
sigaddset(&sigttou, SIGTTOU);
|
||||
sigprocmask(SIG_BLOCK, &sigttou, &chk->oldset);
|
||||
if ( (chk->pid = fork()) < 0 )
|
||||
return close(pipe_fds[0]), close(pipe_fds[1]), false;
|
||||
int success = -2;
|
||||
if ( chk->pid == 0 )
|
||||
{
|
||||
sigdelset(&chk->oldset, SIGINT);
|
||||
sigdelset(&chk->oldset, SIGQUIT);
|
||||
signal(SIGINT, SIG_DFL);
|
||||
signal(SIGQUIT, SIG_DFL);
|
||||
unsigned int termmode = TERMMODE_UNICODE | TERMMODE_SIGNAL |
|
||||
TERMMODE_UTF8 | TERMMODE_LINEBUFFER |
|
||||
TERMMODE_ECHO;
|
||||
if ( restrict_termmode )
|
||||
termmode = TERMMODE_SIGNAL;
|
||||
if ( setpgid(0, 0) < 0 ||
|
||||
close(pipe_fds[0]) < 0 ||
|
||||
tcsetpgrp(0, getpgid(0)) ||
|
||||
sigprocmask(SIG_SETMASK, &chk->oldset, NULL) < 0 ||
|
||||
settermmode(0, termmode) < 0 ||
|
||||
!check_real(username, password) ||
|
||||
write(pipe_fds[1], &success, sizeof(success)) < 0 )
|
||||
{
|
||||
assert(1 <= errno);
|
||||
write(pipe_fds[1], &errno, sizeof(errno));
|
||||
}
|
||||
_exit(0);
|
||||
}
|
||||
close(pipe_fds[1]);
|
||||
chk->pipe = pipe_fds[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
bool check_end(struct check* chk, bool* result, bool try)
|
||||
{
|
||||
if ( try && !chk->pipe_nonblock )
|
||||
{
|
||||
fcntl(chk->pipe, F_SETFL, fcntl(chk->pipe, F_GETFL) | O_NONBLOCK);
|
||||
chk->pipe_nonblock = true;
|
||||
}
|
||||
while ( chk->errnum_done < sizeof(chk->errnum_bytes) )
|
||||
{
|
||||
ssize_t amount = read(chk->pipe, chk->errnum_bytes + chk->errnum_done,
|
||||
sizeof(chk->errnum_bytes) - chk->errnum_done);
|
||||
if ( amount <= 0 )
|
||||
{
|
||||
if ( amount == 0 )
|
||||
errno = EOF;
|
||||
break;
|
||||
}
|
||||
chk->errnum_done += amount;
|
||||
}
|
||||
int code;
|
||||
pid_t wait_ret = waitpid(chk->pid, &code, try ? WNOHANG : 0);
|
||||
if ( try && wait_ret == 0 )
|
||||
return false;
|
||||
tcsetattr(0, TCSAFLUSH, &chk->tio);
|
||||
tcsetpgrp(0, getpgid(0));
|
||||
sigprocmask(SIG_SETMASK, &chk->oldset, NULL);
|
||||
if ( wait_ret < 0 )
|
||||
return *result = false, true;
|
||||
if ( chk->errnum_done < sizeof(chk->errnum_bytes) )
|
||||
chk->errnum = EEOF;
|
||||
if ( WIFSIGNALED(code) )
|
||||
chk->errnum = EINTR;
|
||||
else if ( !(WIFEXITED(code) && WEXITSTATUS(code) == 0) )
|
||||
chk->errnum = EINVAL;
|
||||
int success = -2;
|
||||
if ( chk->errnum < 1 && chk->errnum != success )
|
||||
chk->errnum = EINVAL;
|
||||
if ( chk->errnum != success )
|
||||
return errno = chk->errnum, *result = false, true;
|
||||
return *result = true, true;
|
||||
}
|
||||
|
||||
bool check(const char* username, const char* password)
|
||||
{
|
||||
struct check chk;
|
||||
if ( !check_begin(&chk, username, password, false) )
|
||||
return false;
|
||||
bool result;
|
||||
check_end(&chk, &result, false);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int setcloexecfrom(int from)
|
||||
{
|
||||
int fd = from - 1;
|
||||
while ( (fd = fcntl(fd, F_NEXTFD)) != -1 )
|
||||
{
|
||||
int flags = fcntl(fd, F_GETFD);
|
||||
if ( flags < 0 )
|
||||
return -1;
|
||||
if ( !(flags & FD_CLOEXEC) && fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0 )
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool login(const char* username)
|
||||
{
|
||||
char login_pid[sizeof(pid_t) * 3];
|
||||
snprintf(login_pid, sizeof(login_pid), "%" PRIiPID, getpid());
|
||||
struct passwd* pwd = getpwnam(username);
|
||||
if ( !pwd )
|
||||
return false;
|
||||
struct termios tio;
|
||||
if ( tcgetattr(0, &tio) )
|
||||
return false;
|
||||
int pipe_fds[2];
|
||||
if ( pipe2(pipe_fds, O_CLOEXEC) < 0 )
|
||||
return false;
|
||||
sigset_t oldset, sigttou;
|
||||
sigemptyset(&sigttou);
|
||||
sigaddset(&sigttou, SIGTTOU);
|
||||
sigprocmask(SIG_BLOCK, &sigttou, &oldset);
|
||||
pid_t child_pid = fork();
|
||||
if ( child_pid < 0 )
|
||||
return close(pipe_fds[0]), close(pipe_fds[1]), false;
|
||||
if ( child_pid == 0 )
|
||||
{
|
||||
sigdelset(&oldset, SIGINT);
|
||||
sigdelset(&oldset, SIGQUIT);
|
||||
sigdelset(&oldset, SIGTSTP);
|
||||
signal(SIGINT, SIG_DFL);
|
||||
signal(SIGQUIT, SIG_DFL);
|
||||
(void) (
|
||||
setpgid(0, 0) < 0 ||
|
||||
close(pipe_fds[0]) < 0 ||
|
||||
setgid(pwd->pw_gid) < 0 ||
|
||||
setuid(pwd->pw_uid) < 0 ||
|
||||
setenv("LOGIN_PID", login_pid, 1) < 0 ||
|
||||
setenv("LOGNAME", pwd->pw_name, 1) < 0 ||
|
||||
setenv("USER", pwd->pw_name, 1) < 0 ||
|
||||
chdir(pwd->pw_dir) < 0 ||
|
||||
setenv("HOME", pwd->pw_dir, 1) < 0 ||
|
||||
setenv("SHELL", pwd->pw_shell, 1) < 0 ||
|
||||
close(0) < 0 ||
|
||||
close(1) < 0 ||
|
||||
close(2) < 0 ||
|
||||
open("/dev/tty", O_RDONLY) != 0 ||
|
||||
open("/dev/tty", O_WRONLY) != 1 ||
|
||||
open("/dev/tty", O_WRONLY) != 2 ||
|
||||
setcloexecfrom(3) < 0 ||
|
||||
tcsetpgrp(0, getpgid(0)) ||
|
||||
sigprocmask(SIG_SETMASK, &oldset, NULL) < 0 ||
|
||||
settermmode(0, TERMMODE_NORMAL) < 0 ||
|
||||
execlp(pwd->pw_shell, pwd->pw_shell, (const char*) NULL));
|
||||
write(pipe_fds[1], &errno, sizeof(errno));
|
||||
_exit(127);
|
||||
}
|
||||
close(pipe_fds[1]);
|
||||
int errnum;
|
||||
if ( readall(pipe_fds[0], &errnum, sizeof(errnum)) < (ssize_t) sizeof(errnum) )
|
||||
errnum = 0;
|
||||
close(pipe_fds[0]);
|
||||
int child_status;
|
||||
if ( waitpid(child_pid, &child_status, 0) < 0 )
|
||||
errnum = errno;
|
||||
tcsetattr(0, TCSAFLUSH, &tio);
|
||||
tcsetpgrp(0, getpgid(0));
|
||||
sigprocmask(SIG_SETMASK, &oldset, NULL);
|
||||
dprintf(1, "\e[H\e[2J");
|
||||
fsync(1);
|
||||
if ( errnum != 0 )
|
||||
return errno = errnum, false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool read_terminal_line(char* buffer, size_t size)
|
||||
{
|
||||
assert(size);
|
||||
size--;
|
||||
sigset_t intset;
|
||||
sigemptyset(&intset);
|
||||
sigaddset(&intset, SIGINT);
|
||||
sigaddset(&intset, SIGQUIT);
|
||||
bool newline = false;
|
||||
size_t sofar = 0;
|
||||
while ( !newline && sofar < size )
|
||||
{
|
||||
sigset_t oldset;
|
||||
sigprocmask(SIG_UNBLOCK, &intset, &oldset);
|
||||
ssize_t amount = read(0, buffer + sofar, size - sofar);
|
||||
sigprocmask(SIG_SETMASK, &oldset, NULL);
|
||||
if ( amount <= 0 )
|
||||
return false;
|
||||
for ( ssize_t i = 0; i < amount; i++ )
|
||||
{
|
||||
if ( buffer[sofar + i] != '\n' )
|
||||
continue;
|
||||
newline = true;
|
||||
amount = i;
|
||||
break;
|
||||
}
|
||||
sofar += amount;
|
||||
}
|
||||
while ( !newline )
|
||||
{
|
||||
char c;
|
||||
if ( read(0, &c, 1) <= 0 )
|
||||
return false;
|
||||
newline = c == '\n';
|
||||
}
|
||||
buffer[sofar] = '\0';
|
||||
return true;
|
||||
}
|
||||
|
||||
int textual(void)
|
||||
{
|
||||
unsigned int termmode = TERMMODE_UNICODE | TERMMODE_SIGNAL | TERMMODE_UTF8 |
|
||||
TERMMODE_LINEBUFFER | TERMMODE_ECHO;
|
||||
if ( settermmode(0, termmode) < 0 )
|
||||
err(2, "settermmode");
|
||||
unsigned int pw_termmode = termmode & ~(TERMMODE_ECHO);
|
||||
|
||||
while ( true )
|
||||
{
|
||||
char hostname[HOST_NAME_MAX + 1];
|
||||
hostname[0] = '\0';
|
||||
gethostname(hostname, sizeof(hostname));
|
||||
printf("%s login: ", hostname);
|
||||
fflush(stdout);
|
||||
char username[256];
|
||||
errno = 0;
|
||||
if ( !read_terminal_line(username, sizeof(username)) )
|
||||
{
|
||||
printf("\n");
|
||||
if ( errno && errno != EINTR )
|
||||
{
|
||||
warn("fgets");
|
||||
sleep(1);
|
||||
}
|
||||
printf("\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !strcmp(username, "exit") )
|
||||
exit(0);
|
||||
if ( !strcmp(username, "poweroff") )
|
||||
exit(0);
|
||||
if ( !strcmp(username, "reboot") )
|
||||
exit(1);
|
||||
|
||||
if ( settermmode(0, pw_termmode) < 0 )
|
||||
err(2, "settermmode");
|
||||
printf("Password (will not echo): ");
|
||||
fflush(stdout);
|
||||
char password[256];
|
||||
errno = 0;
|
||||
bool password_success = read_terminal_line(password, sizeof(password));
|
||||
printf("\n");
|
||||
if ( settermmode(0, termmode) < 0 )
|
||||
err(2, "settermmode");
|
||||
if ( !password_success )
|
||||
{
|
||||
if ( errno && errno != EINTR )
|
||||
{
|
||||
warn("fgets");
|
||||
sleep(1);
|
||||
}
|
||||
printf("\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
bool result = check(username, password);
|
||||
explicit_bzero(password, sizeof(password));
|
||||
if ( !result )
|
||||
{
|
||||
const char* msg = "Invalid username/password";
|
||||
if ( errno != EACCES )
|
||||
msg = strerror(errno);
|
||||
printf("%s\n", msg);
|
||||
printf("\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !login(username) )
|
||||
{
|
||||
warn("logging in as %s", username);
|
||||
printf("\n");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
setlocale(LC_ALL, "");
|
||||
if ( getuid() != 0 )
|
||||
errx(2, "must be user root");
|
||||
if ( getgid() != 0 )
|
||||
errx(2, "must be group root");
|
||||
if ( !isatty(0) )
|
||||
{
|
||||
close(0);
|
||||
if ( open("/dev/tty", O_RDONLY) != 0 )
|
||||
err(2, "/dev/tty");
|
||||
}
|
||||
if ( !isatty(1) )
|
||||
{
|
||||
close(1);
|
||||
if ( open("/dev/tty", O_WRONLY) != 0 )
|
||||
err(2, "/dev/tty");
|
||||
}
|
||||
if ( !isatty(2) )
|
||||
{
|
||||
if ( dup2(1, 2) < 0 )
|
||||
err(2, "dup2");
|
||||
}
|
||||
if ( tcgetpgrp(0) != getpgid(0) )
|
||||
errx(2, "must be in foreground process group");
|
||||
if ( getpgid(0) != getpid() )
|
||||
errx(2, "must be progress group leader");
|
||||
struct sigaction sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sa_handler = on_interrupt_signal;
|
||||
sigaddset(&sa.sa_mask, SIGINT);
|
||||
sigaddset(&sa.sa_mask, SIGQUIT);
|
||||
sigaction(SIGINT, &sa, NULL);
|
||||
sigaction(SIGQUIT, &sa, NULL);
|
||||
sigaddset(&sa.sa_mask, SIGTSTP);
|
||||
sigprocmask(SIG_BLOCK, &sa.sa_mask, NULL);
|
||||
int result = -1;
|
||||
if ( result == -1 )
|
||||
result = graphical();
|
||||
if ( result == -1 )
|
||||
result = textual();
|
||||
return result;
|
||||
}
|
52
login/login.h
Normal file
52
login/login.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2014, 2015.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation, either version 3 of the License, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
login.h
|
||||
Authenticates users.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef LOGIN_H
|
||||
#define LOGIN_H
|
||||
|
||||
struct check
|
||||
{
|
||||
sigset_t oldset;
|
||||
struct termios tio;
|
||||
pid_t pid;
|
||||
int pipe;
|
||||
union
|
||||
{
|
||||
int errnum;
|
||||
unsigned char errnum_bytes[sizeof(int)];
|
||||
};
|
||||
unsigned int errnum_done;
|
||||
bool pipe_nonblock;
|
||||
};
|
||||
|
||||
bool login(const char* username);
|
||||
bool check_real(const char* username, const char* password);
|
||||
bool check_begin(struct check* chk,
|
||||
const char* username,
|
||||
const char* password,
|
||||
bool restrict_termmode);
|
||||
bool check_end(struct check* chk, bool* result, bool try);
|
||||
bool check(const char* username, const char* password);
|
||||
int graphical(void);
|
||||
int textual(void);
|
||||
|
||||
#endif
|
41
login/pixel.c
Normal file
41
login/pixel.c
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2014, 2015.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation, either version 3 of the License, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
pixel.c
|
||||
Pixel utilities.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "pixel.h"
|
||||
|
||||
uint32_t blend_pixel(uint32_t bg_value, uint32_t fg_value)
|
||||
{
|
||||
union color_rgba8 fg; fg.value = fg_value;
|
||||
union color_rgba8 bg; bg.value = bg_value;
|
||||
if ( fg.a == 255 )
|
||||
return fg.value;
|
||||
if ( fg.a == 0 )
|
||||
return bg.value;
|
||||
union color_rgba8 ret;
|
||||
ret.a = 255;
|
||||
ret.r = ((255-fg.a)*bg.r + fg.a*fg.r) / 256;
|
||||
ret.g = ((255-fg.a)*bg.g + fg.a*fg.g) / 256;
|
||||
ret.b = ((255-fg.a)*bg.b + fg.a*fg.b) / 256;
|
||||
return ret.value;
|
||||
}
|
60
login/pixel.h
Normal file
60
login/pixel.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2014, 2015.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation, either version 3 of the License, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
pixel.h
|
||||
Pixel utilities.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef PIXEL_H
|
||||
#define PIXEL_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// TODO: This isn't the only pixel format in the world!
|
||||
union color_rgba8
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint8_t b;
|
||||
uint8_t g;
|
||||
uint8_t r;
|
||||
uint8_t a;
|
||||
};
|
||||
uint32_t value;
|
||||
};
|
||||
|
||||
__attribute__((used))
|
||||
static inline uint32_t make_color_a(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
|
||||
{
|
||||
union color_rgba8 color;
|
||||
color.r = r;
|
||||
color.g = g;
|
||||
color.b = b;
|
||||
color.a = a;
|
||||
return color.value;
|
||||
}
|
||||
|
||||
__attribute__((used))
|
||||
static inline uint32_t make_color(uint8_t r, uint8_t g, uint8_t b)
|
||||
{
|
||||
return make_color_a(r, g, b, 255);
|
||||
}
|
||||
|
||||
uint32_t blend_pixel(uint32_t bg_value, uint32_t fg_value);
|
||||
|
||||
#endif
|
75
login/vgafont.c
Normal file
75
login/vgafont.c
Normal file
|
@ -0,0 +1,75 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2014, 2015.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation, either version 3 of the License, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
vgafont.c
|
||||
VGA font.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <ioleast.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "framebuffer.h"
|
||||
#include "vgafont.h"
|
||||
|
||||
unsigned char font[FONT_CHARSIZE * FONT_NUMCHARS];
|
||||
|
||||
bool load_font()
|
||||
{
|
||||
static bool done = false;
|
||||
if ( done )
|
||||
return true;
|
||||
int fd = open("/dev/vgafont", O_RDONLY);
|
||||
if ( fd < 0 )
|
||||
return false;
|
||||
if ( readall(fd, font, sizeof(font)) != sizeof(font) )
|
||||
return false;
|
||||
close(fd);
|
||||
return done = true;
|
||||
}
|
||||
|
||||
void render_char(struct framebuffer fb, char c, uint32_t color)
|
||||
{
|
||||
unsigned char uc = (unsigned char) c;
|
||||
|
||||
uint32_t buffer[FONT_HEIGHT * (FONT_WIDTH+1)];
|
||||
for ( size_t y = 0; y < FONT_HEIGHT; y++ )
|
||||
{
|
||||
unsigned char line_bitmap = font[uc * FONT_CHARSIZE + y];
|
||||
for ( size_t x = 0; x < FONT_WIDTH; x++ )
|
||||
buffer[y * (FONT_WIDTH+1) + x] = line_bitmap & 1U << (7 - x) ? color : 0;
|
||||
buffer[y * (FONT_WIDTH+1) + 8] = 0; //line_bitmap & 1U << 0 ? color : 0;
|
||||
}
|
||||
|
||||
struct framebuffer character_fb;
|
||||
character_fb.xres = FONT_WIDTH + 1;
|
||||
character_fb.yres = FONT_HEIGHT;
|
||||
character_fb.pitch = character_fb.xres;
|
||||
character_fb.buffer = buffer;
|
||||
|
||||
framebuffer_copy_to_framebuffer_blend(fb, character_fb);
|
||||
}
|
||||
|
||||
void render_text(struct framebuffer fb, const char* str, uint32_t color)
|
||||
{
|
||||
for ( size_t i = 0; str[i]; i++ )
|
||||
render_char(framebuffer_crop(fb, (FONT_WIDTH+1) * i, 0, fb.xres, fb.yres),
|
||||
str[i], color);
|
||||
}
|
41
login/vgafont.h
Normal file
41
login/vgafont.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2014, 2015.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation, either version 3 of the License, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
vgafont.h
|
||||
VGA font.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef VGAFONT_H
|
||||
#define VGAFONT_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "framebuffer.h"
|
||||
|
||||
#define FONT_WIDTH 8
|
||||
#define FONT_HEIGHT 16
|
||||
#define FONT_NUMCHARS 256
|
||||
#define FONT_CHARSIZE ((FONT_WIDTH * FONT_HEIGHT) / 8)
|
||||
|
||||
extern uint8_t font[FONT_CHARSIZE * FONT_NUMCHARS];
|
||||
|
||||
bool load_font();
|
||||
void render_char(struct framebuffer fb, char c, uint32_t color);
|
||||
void render_text(struct framebuffer fb, const char* str, uint32_t color);
|
||||
|
||||
#endif
|
|
@ -13,9 +13,16 @@ You will be presented a with standard Unix command line environment upon booting
|
|||
the live environment.
|
||||
.Ss Shutdown
|
||||
.Xr init 8
|
||||
spawns a session after boot. This is a root shell if booted in
|
||||
spawns a session after boot. This is
|
||||
.Xr login 8
|
||||
if the system is booted in multi-user mode. This is a root shell if booted in
|
||||
single-user mode.
|
||||
.Pp
|
||||
To power off from the login screen, login as user
|
||||
.Sy poweroff .
|
||||
To reboot, login as user
|
||||
.Sy reboot .
|
||||
.Pp
|
||||
To power off from a single-user boot root shell, run
|
||||
.Sy exit 0
|
||||
in the shell. To reboot, run
|
||||
|
|
|
@ -780,7 +780,9 @@ class action** administration::list_actions(size_t* num_actions)
|
|||
{
|
||||
class action** actions = new action*[4 + 1];
|
||||
size_t index = 0;
|
||||
#if 0 // TODO: Until crypt_newhash is used for the password.
|
||||
actions[index++] = new action("Create user", new create_user());
|
||||
#endif
|
||||
actions[index++] = new action("Enable Runes", new decide_runes(true));
|
||||
actions[index++] = new action("Disable Runes", new decide_runes(false));
|
||||
actions[index++] = new action("Trinit Core", new core());
|
||||
|
@ -788,6 +790,23 @@ class action** administration::list_actions(size_t* num_actions)
|
|||
return *num_actions = index, actions;
|
||||
}
|
||||
|
||||
class exiter : public object
|
||||
{
|
||||
public:
|
||||
exiter() { }
|
||||
virtual ~exiter() { }
|
||||
|
||||
public:
|
||||
virtual enum object_type type() { return TYPE_FILE; }
|
||||
virtual void invoke();
|
||||
|
||||
};
|
||||
|
||||
void exiter::invoke()
|
||||
{
|
||||
exit(0);
|
||||
}
|
||||
|
||||
class desktop : public object
|
||||
{
|
||||
public:
|
||||
|
@ -811,7 +830,7 @@ class action** desktop::list_actions(size_t* num_actions)
|
|||
actions[3] = new action("Shell", new path_program("sh"));
|
||||
actions[4] = new action("Development", new development());
|
||||
actions[5] = new action("Administration", new administration());
|
||||
actions[6] = new action("Logout", parent_object);
|
||||
actions[6] = new action("Logout", new exiter());
|
||||
return actions;
|
||||
}
|
||||
|
||||
|
@ -830,109 +849,6 @@ class object* log_user_in(struct passwd* user)
|
|||
return new desktop();
|
||||
}
|
||||
|
||||
class poweroff : public object
|
||||
{
|
||||
public:
|
||||
poweroff() { }
|
||||
virtual ~poweroff() { }
|
||||
|
||||
public:
|
||||
virtual enum object_type type() { return TYPE_FILE; }
|
||||
virtual void invoke();
|
||||
|
||||
};
|
||||
|
||||
void poweroff::invoke()
|
||||
{
|
||||
exit(0);
|
||||
}
|
||||
|
||||
class login : public object
|
||||
{
|
||||
public:
|
||||
login(const char* username) : username(strdup(username)) { }
|
||||
virtual ~login() { }
|
||||
|
||||
public:
|
||||
virtual enum object_type type() { return TYPE_DIRECTORY; }
|
||||
virtual class object* factory();
|
||||
virtual const char* title() { return "Authentication required "; }
|
||||
virtual const char* prompt() { return "Enter Password:"; }
|
||||
virtual bool is_password_prompt() { return true; }
|
||||
virtual class action** list_actions(size_t* num_actions);
|
||||
virtual class object* command_line(const char* command);
|
||||
|
||||
private:
|
||||
char* username;
|
||||
|
||||
};
|
||||
|
||||
class user_selection : public object
|
||||
{
|
||||
public:
|
||||
user_selection() { }
|
||||
virtual ~user_selection() { }
|
||||
|
||||
public:
|
||||
virtual enum object_type type() { return TYPE_DIRECTORY; }
|
||||
virtual const char* title() { return "User Selection"; }
|
||||
virtual class action** list_actions(size_t* num_actions);
|
||||
virtual class object* command_line(const char* command);
|
||||
|
||||
};
|
||||
|
||||
class object* login::factory()
|
||||
{
|
||||
if ( struct passwd* user = getpwnam(username) )
|
||||
if ( !user->pw_passwd[0] )
|
||||
return log_user_in(user);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
class action** login::list_actions(size_t* num_actions)
|
||||
{
|
||||
return *num_actions = 0, new class action*[0];
|
||||
}
|
||||
|
||||
class object* login::command_line(const char* password)
|
||||
{
|
||||
error_string = "";
|
||||
if ( struct passwd* user = getpwnam(username) )
|
||||
{
|
||||
if ( !strcmp(user->pw_passwd, password) )
|
||||
return log_user_in(user);
|
||||
else
|
||||
return error_string = "Invalid password", (class object*) NULL;
|
||||
}
|
||||
return error_string = "No such user", (class object*) NULL;
|
||||
}
|
||||
|
||||
class action** user_selection::list_actions(size_t* num_actions)
|
||||
{
|
||||
size_t num_users = 0;
|
||||
FILE* fp = openpw();
|
||||
while ( fgetpwent(fp) )
|
||||
num_users++;
|
||||
fseeko(fp, 0, SEEK_SET);
|
||||
action** actions = new class action*[num_users + 1];
|
||||
size_t which_user = 0;
|
||||
while ( struct passwd* user = fgetpwent(fp) )
|
||||
actions[which_user++] =
|
||||
new action(user->pw_gecos ? user->pw_gecos : user->pw_name,
|
||||
new login(user->pw_name));
|
||||
fclose(fp);
|
||||
actions[num_users] = new action("Poweroff", new poweroff);
|
||||
return *num_actions = num_users + 1, actions;
|
||||
}
|
||||
|
||||
class object* user_selection::command_line(const char* command)
|
||||
{
|
||||
error_string = "";
|
||||
if ( getpwnam(command) )
|
||||
return new login(command);
|
||||
return error_string = "No such user", (class object*) NULL;
|
||||
}
|
||||
|
||||
class FrameBufferInfo;
|
||||
struct Desktop;
|
||||
struct RenderInfo;
|
||||
|
@ -1764,7 +1680,7 @@ static void InitializeDesktop(struct Desktop* desktop)
|
|||
desktop->rshift = false;
|
||||
desktop->actions = NULL;
|
||||
desktop->num_actions = 0;
|
||||
desktop->object = new user_selection();
|
||||
desktop->object = new class desktop();
|
||||
UpdateActionList(desktop);
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
*******************************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void suggest_editor(const char* filename)
|
||||
|
@ -49,6 +50,40 @@ void suggest_unmount(const char* filename)
|
|||
fprintf(stderr, " Command 'unmount' from package 'utils'\n");
|
||||
}
|
||||
|
||||
void suggest_logout(const char* filename)
|
||||
{
|
||||
fprintf(stderr, "No command '%s' found, did you mean:\n", filename);
|
||||
fprintf(stderr, " Exiting your shell normally to logout.\n");
|
||||
}
|
||||
|
||||
void suggest_poweroff(const char* filename)
|
||||
{
|
||||
fprintf(stderr, "No command '%s' found, did you mean:\n", filename);
|
||||
if ( getenv("LOGIN_PID") )
|
||||
{
|
||||
fprintf(stderr, " Exiting your shell normally to logout.\n");
|
||||
fprintf(stderr, " Login as user 'poweroff' to power off computer.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, " Exiting your shell normally to poweroff.\n");
|
||||
}
|
||||
}
|
||||
|
||||
void suggest_reboot(const char* filename)
|
||||
{
|
||||
fprintf(stderr, "No command '%s' found, did you mean:\n", filename);
|
||||
if ( getenv("LOGIN_PID") )
|
||||
{
|
||||
fprintf(stderr, " Exiting your shell normally to logout.\n");
|
||||
fprintf(stderr, " Login as user 'reboot' to reboot computer.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, " Exiting your shell with 'exit 1' to reboot.\n");
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
const char* filename = 2 <= argc ? argv[1] : argv[0];
|
||||
|
@ -65,6 +100,15 @@ int main(int argc, char* argv[])
|
|||
suggest_extfs(filename);
|
||||
else if ( !strcmp(filename, "umount") )
|
||||
suggest_unmount(filename);
|
||||
else if ( !strcmp(filename, "logout") ||
|
||||
!strcmp(filename, "logoff") )
|
||||
suggest_logout(filename);
|
||||
else if ( !strcmp(filename, "poweroff") ||
|
||||
!strcmp(filename, "halt") ||
|
||||
!strcmp(filename, "shutdown") )
|
||||
suggest_poweroff(filename);
|
||||
else if ( !strcmp(filename, "reboot") )
|
||||
suggest_reboot(filename);
|
||||
fprintf(stderr, "%s: command not found\n", filename);
|
||||
return 127;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue