Schlimm is an attempt to create a fork of the SLiM desktop manager.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

386 lines
10 KiB

/*
* This file is part of schlimm
* Copyright (C) 2019-2021 Moritz Strohm <ncc1988@posteo.de>
* and others (see the AUTHORS file).
*
* slimlock
* Copyright (c) 2010-2012 Joel Burget <joelburget@gmail.com>
*
* 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 2 of the License, or
* (at your option) any later version.
*/
#include "slimlock.h"
#undef APPNAME
#define APPNAME "slimlock"
#define SLIMLOCKCFG SYSCONFDIR"/slimlock.conf"
using namespace std;
// I really didn't wanna put these globals here, but it's the only way...
Display* dpy;
int scr;
Window win;
Schlimm::Config* cfg;
Schlimm::Panel* loginPanel;
string themeName = "";
pam_handle_t *pam_handle;
struct pam_conv conv = {ConvCallback, NULL};
CARD16 dpms_standby, dpms_suspend, dpms_off, dpms_level;
BOOL dpms_state, using_dpms;
int term;
int main(int argc, char **argv)
{
Schlimm::Log::init(APPNAME);
if ((argc == 2) && !strcmp("-v", argv[1])) {
std::cerr << APPNAME << "-" << VERSION << ", © 2010-2012 Joel Burget, 2021 Moritz Strohm and others" << std::endl;
return EXIT_SUCCESS;
} else if(argc != 1) {
std::cerr << "Usage: " << APPNAME << " [-v]" << std::endl;
return EXIT_SUCCESS;
}
void (*prev_fn)(int);
// restore DPMS settings should slimlock be killed in the line of duty
prev_fn = signal(SIGTERM, HandleSignal);
if (prev_fn == SIG_IGN) {
signal(SIGTERM, SIG_IGN);
}
// create a lock file to solve mutliple instances problem
// /var/lock used to be the place to put this, now it's /run/lock
// ...i think
struct stat statbuf;
int lock_file;
// try /run/lock first, since i believe it's preferred
if (!stat("/run/lock", &statbuf)) {
lock_file = open("/run/lock/" APPNAME ".lock", O_CREAT | O_RDWR, 0666);
} else {
lock_file = open("/var/lock/" APPNAME ".lock", O_CREAT | O_RDWR, 0666);
}
int rc = flock(lock_file, LOCK_EX | LOCK_NB);
if (rc) {
if (EWOULDBLOCK == errno) {
Schlimm::Log::log("This program is already running!");
return EXIT_FAILURE;
}
}
unsigned int cfg_passwd_timeout;
// Read user's current theme
cfg = new Schlimm::Config;
cfg->readConf(CFGFILE);
cfg->readConf(SLIMLOCKCFG);
string themebase = "";
string themefile = "";
string themedir = "";
themeName = "";
themebase = string(THEMESDIR) + "/";
themeName = cfg->getOption("current_theme");
string::size_type pos;
if ((pos = themeName.find(",")) != string::npos) {
themeName = findValidRandomTheme(themeName);
}
bool loaded = false;
while (!loaded) {
themedir = themebase + themeName;
themefile = themedir + THEMESFILE;
if (!cfg->readConf(themefile)) {
if (themeName == "default") {
Schlimm::Log::log(
fmt::format("Failed to open default theme file {}!", themefile)
);
exit(ERR_EXIT);
} else {
Schlimm::Log::log(
fmt::format("The theme name \"{}\" in the configuration is invalid. The default theme name will be used instead.", themeName)
);
themeName = "default";
}
} else {
loaded = true;
}
}
const char *display = getenv("DISPLAY");
if (!display) {
display = DISPLAY;
}
if (!(dpy = XOpenDisplay(display))) {
Schlimm::Log::log("The display cannot be opened!");
return EXIT_FAILURE;
}
scr = DefaultScreen(dpy);
XSetWindowAttributes wa;
wa.override_redirect = 1;
wa.background_pixel = BlackPixel(dpy, scr);
// Create a full screen window
Window root = RootWindow(dpy, scr);
win = XCreateWindow(
dpy,
root,
0,
0,
DisplayWidth(dpy, scr),
DisplayHeight(dpy, scr),
0,
DefaultDepth(dpy, scr),
CopyFromParent,
DefaultVisual(dpy, scr),
CWOverrideRedirect | CWBackPixel,
&wa
);
XMapWindow(dpy, win);
XFlush(dpy);
for (int len = 1000; len; len--) {
if (XGrabKeyboard(dpy, root, True, GrabModeAsync, GrabModeAsync, CurrentTime) == GrabSuccess) {
break;
}
usleep(1000);
}
XSelectInput(dpy, win, ExposureMask | KeyPressMask);
// This hides the cursor if the user has that option enabled in their
// configuration
HideCursor();
loginPanel = new Schlimm::Panel(dpy, scr, win, cfg, themedir, Schlimm::Panel::Mode_Lock);
int ret = pam_start(APPNAME, loginPanel->getName().c_str(), &conv, &pam_handle);
// If we can't start PAM, just exit because slimlock won't work right
if (ret != PAM_SUCCESS) {
Schlimm::Log::log(fmt::format("PAM error: {}", pam_strerror(pam_handle, ret)));
return EXIT_FAILURE;
}
// disable tty switching
if (cfg->getOption("tty_lock") == "1") {
if ((term = open("/dev/console", O_RDWR)) == -1) {
Schlimm::Log::log("Error opening the console!");
}
if ((ioctl(term, VT_LOCKSWITCH)) == -1) {
Schlimm::Log::log("Error locking the console!");
}
}
// Set up DPMS
unsigned int cfg_dpms_standby, cfg_dpms_off;
cfg_dpms_standby = Schlimm::Config::string2int(cfg->getOption("dpms_standby_timeout").c_str());
cfg_dpms_off = Schlimm::Config::string2int(cfg->getOption("dpms_off_timeout").c_str());
using_dpms = DPMSCapable(dpy) && (cfg_dpms_standby > 0);
if (using_dpms) {
DPMSGetTimeouts(dpy, &dpms_standby, &dpms_suspend, &dpms_off);
DPMSSetTimeouts(
dpy,
cfg_dpms_standby,
cfg_dpms_standby,
cfg_dpms_off
);
DPMSInfo(dpy, &dpms_level, &dpms_state);
if (!dpms_state) {
DPMSEnable(dpy);
}
}
// Get password timeout
cfg_passwd_timeout = Schlimm::Config::string2int(cfg->getOption("wrong_passwd_timeout").c_str());
// Let's just make sure it has a sane value
cfg_passwd_timeout = cfg_passwd_timeout > 60 ? 60 : cfg_passwd_timeout;
pthread_t raise_thread;
pthread_create(&raise_thread, NULL, RaiseWindow, NULL);
// Main loop
while (true) {
loginPanel->resetPasswd();
// AuthenticateUser returns true if authenticated
if (AuthenticateUser()) {
break;
}
loginPanel->wrongPassword(cfg_passwd_timeout);
}
// kill thread before destroying the window that it's supposed to be raising
pthread_cancel(raise_thread);
loginPanel->closePanel();
delete loginPanel;
// Get DPMS stuff back to normal
if (using_dpms) {
DPMSSetTimeouts(dpy, dpms_standby, dpms_suspend, dpms_off);
// turn off DPMS if it was off when we entered
if (!dpms_state) {
DPMSDisable(dpy);
}
}
XCloseDisplay(dpy);
close(lock_file);
if(cfg->getOption("tty_lock") == "1") {
if ((ioctl(term, VT_UNLOCKSWITCH)) == -1) {
Schlimm::Log::log("Error unlocking the console!");
}
}
close(term);
return 0;
}
void HideCursor()
{
if (cfg->getOption("hidecursor") == "true") {
XColor black;
char cursordata[1];
Pixmap cursorpixmap;
Cursor cursor;
cursordata[0] = 0;
cursorpixmap = XCreateBitmapFromData(dpy, win, cursordata, 1, 1);
black.red = 0;
black.green = 0;
black.blue = 0;
cursor = XCreatePixmapCursor(
dpy,
cursorpixmap,
cursorpixmap,
&black,
&black,
0,
0
);
XFreePixmap(dpy, cursorpixmap);
XDefineCursor(dpy, win, cursor);
}
}
static int ConvCallback(
int num_msgs,
const struct pam_message **msg,
struct pam_response **resp,
void *appdata_ptr
)
{
loginPanel->eventHandler(Schlimm::Panel::Get_Passwd);
// PAM expects an array of responses, one for each message
if (num_msgs == 0 ||
(*resp = (pam_response*) calloc(num_msgs, sizeof(struct pam_message))) == NULL) {
return PAM_BUF_ERR;
}
for (int i = 0; i < num_msgs; i++) {
if (msg[i]->msg_style != PAM_PROMPT_ECHO_OFF &&
msg[i]->msg_style != PAM_PROMPT_ECHO_ON) {
continue;
}
// return code is currently not used but should be set to zero
resp[i]->resp_retcode = 0;
if ((resp[i]->resp = strdup(loginPanel->getPasswd().c_str())) == NULL) {
free(*resp);
return PAM_BUF_ERR;
}
}
return PAM_SUCCESS;
}
bool AuthenticateUser()
{
return(pam_authenticate(pam_handle, 0) == PAM_SUCCESS);
}
string findValidRandomTheme(const string& set)
{
// extract random theme from theme set; return empty string on error
string name = set;
struct stat buf;
if (name[name.length() - 1] == ',') {
name.erase(name.length() - 1);
}
Util::srandom(Util::makeseed());
vector<string> themes;
string themefile;
Schlimm::Config::split(themes, name, ',');
do {
int sel = Util::random() % themes.size();
name = Schlimm::Config::Trim(themes[sel]);
themefile = string(THEMESDIR) +"/" + name + THEMESFILE;
if (stat(themefile.c_str(), &buf) != 0) {
themes.erase(find(themes.begin(), themes.end(), name));
cerr << APPNAME << ": Invalid theme in config: "
<< name << endl;
name = "";
}
} while (name == "" && themes.size());
return name;
}
void HandleSignal(int sig)
{
// Get DPMS stuff back to normal
if (using_dpms) {
DPMSSetTimeouts(dpy, dpms_standby, dpms_suspend, dpms_off);
// turn off DPMS if it was off when we entered
if (!dpms_state) {
DPMSDisable(dpy);
}
}
if ((ioctl(term, VT_UNLOCKSWITCH)) == -1) {
Schlimm::Log::log("Error unlocking the console!");
}
close(term);
loginPanel->closePanel();
delete loginPanel;
Schlimm::Log::log("Signal caught. This process will die.");
exit(EXIT_FAILURE);
}
void* RaiseWindow(void *data)
{
while(1) {
XRaiseWindow(dpy, win);
sleep(1);
}
return (void *)0;
}