Imported Upstream version 0.2.0
[debian/lxdm.git] / src / lxdm.c
CommitLineData
eea1c851
AL
1/*
2 * lxdm.c - main entry of lxdm
3 *
4 * Copyright 2009 dgod <dgod.osa@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 * MA 02110-1301, USA.
20 */
21
22#define _GNU_SOURCE
23
24#ifdef HAVE_CONFIG_H
25#include <config.h>
26#endif
27#ifndef HAVE_LIBPAM
28#define HAVE_LIBPAM 0
29#endif
30#ifndef HAVE_LIBXMU
31#define HAVE_LIBXMU 0
32#endif
33
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <ctype.h>
38#include <unistd.h>
39#include <stdarg.h>
40#include <fcntl.h>
41#include <pwd.h>
42#include <grp.h>
43#include <shadow.h>
44#include <errno.h>
45#include <signal.h>
46#include <time.h>
47#include <sys/wait.h>
48#include <glib.h>
49#include <gdk/gdk.h>
50#include <gdk/gdkx.h>
51#include <X11/Xlib.h>
faee902e 52#include <X11/XKBlib.h>
eea1c851 53
faee902e 54#include <sys/vt.h>
eea1c851 55#include <sys/ioctl.h>
faee902e 56#include <sys/stat.h>
eea1c851 57
faee902e
AL
58#include <execinfo.h>
59
60#include <utmp.h>
eea1c851
AL
61
62#if HAVE_LIBPAM
63#include <security/pam_appl.h>
64#endif
65
66#if HAVE_LIBCK_CONNECTOR
faee902e
AL
67#include <ck-connector.h>
68#endif
69
70#if HAVE_LIBXAU
71#include <X11/Xauth.h>
72#include <sys/utsname.h>
eea1c851
AL
73#endif
74
75#include "lxdm.h"
76
77GKeyFile *config;
78static pid_t server;
faee902e 79static guint server_watch;
eea1c851
AL
80#if HAVE_LIBCK_CONNECTOR
81static CkConnector *ckc;
82#endif
83static Window *my_xid;
84static unsigned int my_xid_n;
85static char *self;
86static pid_t child;
87static int reason;
faee902e
AL
88static int old_tty=1,tty = 7;
89
90#ifndef DISABLE_XAUTH
eea1c851 91static char mcookie[33];
faee902e
AL
92#endif
93
94#if HAVE_LIBXAU
95static Xauth x_auth;
96#endif
eea1c851 97
faee902e 98static int get_active_vt(void)
eea1c851 99{
faee902e
AL
100 int console_fd;
101 struct vt_stat console_state = { 0 };
eea1c851 102
faee902e 103 console_fd = open("/dev/console", O_RDONLY | O_NOCTTY);
eea1c851 104
faee902e
AL
105 if( console_fd < 0 )
106 goto out;
eea1c851 107
faee902e
AL
108 if( ioctl(console_fd, VT_GETSTATE, &console_state) < 0 )
109 goto out;
eea1c851
AL
110
111out:
faee902e
AL
112 if( console_fd >= 0 )
113 close(console_fd);
114
115 return console_state.v_active;
116}
eea1c851 117
faee902e
AL
118static void set_active_vt(int vt)
119{
120 int fd;
121
122 fd = open("/dev/console", O_RDWR);
123 if( fd < 0 )
124 fd = 0;
125 ioctl(fd, VT_ACTIVATE, vt);
126 if( fd != 0 )
127 close(fd);
eea1c851
AL
128}
129
faee902e 130static gboolean plymouth_is_running(void)
eea1c851 131{
faee902e
AL
132 int status;
133 gboolean res;
eea1c851 134
faee902e
AL
135 res=g_spawn_command_line_sync ("/bin/plymouth --ping",NULL,NULL,&status,NULL);
136 if(!res) return FALSE;
137 return WIFEXITED (status) && WEXITSTATUS (status) == 0;
138}
139
140static void plymouth_quit_with_transition(void)
141{
142 g_spawn_command_line_sync("/bin/plymouth quit --retain-splash",NULL,NULL,NULL,NULL);
143}
144
145static void plymouth_quit_without_transition(void)
146{
147 g_spawn_command_line_sync("/bin/plymouth quit --retain-splash",NULL,NULL,NULL,NULL);
148}
149
150static void plymouth_prepare_transition(void)
151{
152 g_spawn_command_line_sync ("/bin/plymouth deactivate",NULL,NULL,NULL,NULL);
eea1c851
AL
153}
154
155void lxdm_get_tty(void)
156{
faee902e
AL
157 char *s = g_key_file_get_string(config, "server", "arg", 0);
158 int arc;
159 char **arg;
160 int len;
161 int gotvtarg = 0;
162 int nr = 0;
163 gboolean plymouth;
164
165 plymouth=plymouth_is_running();
166 if(plymouth) plymouth_prepare_transition();
167
168 old_tty=get_active_vt();
169 if( !s ) s = g_strdup("/usr/bin/X");
170 g_shell_parse_argv(s, &arc, &arg, 0);
171 g_free(s);
172 for( len = 0; arg && arg[len]; len++ )
173 {
174 char *p = arg[len];
175 if( !strncmp(p, "vt", 2) && isdigit(p[2]) &&
176 ( !p[3] || (isdigit(p[3]) && !p[4]) ) )
177 {
178 tty = atoi(p + 2);
179 gotvtarg = 1;
180 }
181 }
182 if(!gotvtarg)
183 {
184 /* support plymouth */
185 nr = g_file_test("/var/spool/gdm/force-display-on-active-vt", G_FILE_TEST_EXISTS);
186 if( nr || g_key_file_get_integer(config, "server", "active_vt", 0) )
187 /* use the active vt */
188 tty = old_tty;
189 if( nr ) unlink("/var/spool/gdm/force-display-on-active-vt");
190 if(plymouth)
191 {
192 nr=1;
193 plymouth_quit_with_transition();
eea1c851 194 }
faee902e
AL
195 }
196 else
197 {
198 if(plymouth) /* set tty and plymouth running */
199 plymouth_quit_without_transition();
eea1c851 200 }
faee902e
AL
201 arg = g_renew(char *, arg, len + 10);
202 if( !gotvtarg )
203 arg[len++] = g_strdup_printf("vt%d", tty);
204 arg[len++] = g_strdup("-nolisten");
205 arg[len++] = g_strdup("tcp");
206 if( nr != 0 )
207 arg[len++] = g_strdup("-nr");
208 arg[len] = NULL;
209 s = g_strjoinv(" ", arg);
210 g_strfreev(arg);
211 g_key_file_set_string(config, "server", "arg", s);
212 g_free(s);
213 if(old_tty!=tty)
214 set_active_vt(tty);
eea1c851
AL
215}
216
217void lxdm_restart_self(void)
218{
faee902e
AL
219 reason = 0;
220 exit(0);
eea1c851
AL
221}
222
faee902e 223void lxdm_quit_self(int code)
eea1c851 224{
faee902e
AL
225 reason = (code?code:255);
226 exit(0);
eea1c851
AL
227}
228
faee902e 229void log_print(char *fmt, ...)
eea1c851 230{
faee902e
AL
231 FILE *log;
232 va_list ap;
233 log = fopen("/var/log/lxdm.log", "a");
234 if(!log) return;
235 va_start(ap, fmt);
236 vfprintf(log, fmt, ap);
237 va_end(ap);
238 fclose(log);
eea1c851
AL
239}
240
241GSList *do_scan_xsessions(void)
242{
faee902e
AL
243 GSList *xsessions = NULL;
244 GDir *d;
245 const char *basename;
246 GKeyFile *f;
247
248 d = g_dir_open(XSESSIONS_DIR, 0, NULL);
249 if( !d )
250 return NULL;
251
252 f = g_key_file_new();
253 while( ( basename = g_dir_read_name(d) ) != NULL )
254 {
255 char *file_path;
256 gboolean loaded;
257
258 if(!g_str_has_suffix(basename, ".desktop"))
259 continue;
260
261 file_path = g_build_filename(XSESSIONS_DIR, basename, NULL);
262 loaded = g_key_file_load_from_file(f, file_path, G_KEY_FILE_NONE, NULL);
263 g_free(file_path);
264
265 if( loaded )
266 {
267 char *name = g_key_file_get_locale_string(f, "Desktop Entry", "Name", NULL, NULL);
268 if( name )
269 {
270 char *exec = g_key_file_get_string(f, "Desktop Entry", "Exec", NULL);
271 if(exec)
272 {
273 Session* sess = g_new( Session, 1 );
274 sess->name = name;
275 sess->exec = exec;
276 sess->desktop_file = g_strdup(basename);
277 if( !strcmp(name, "LXDE") )
278 xsessions = g_slist_prepend(xsessions, sess);
279 else
280 xsessions = g_slist_append(xsessions, sess);
281 continue; /* load next file */
282 g_free(exec);
283 }
284 g_free(name);
285 }
286 }
287 }
288 g_dir_close(d);
289 g_key_file_free(f);
290 return xsessions;
eea1c851
AL
291}
292
293void free_xsessions(GSList *l)
294{
faee902e
AL
295 GSList *p;
296 Session *sess;
297
298 for( p = l; p; p = p->next )
299 {
300 sess = p->data;
301 g_free(sess->name);
302 g_free(sess->exec);
303 g_free(sess);
304 }
305 g_slist_free(l);
eea1c851
AL
306}
307
faee902e 308#ifndef DISABLE_XAUTH
eea1c851
AL
309void create_server_auth(void)
310{
faee902e
AL
311 GRand *h;
312 int i;
313 char *authfile;
314 char *tmp;
315
316 h = g_rand_new();
317#if HAVE_LIBXAU
318 for (i=0;i<16;i++)
319 mcookie[i]=(char)g_rand_int(h);
320#else
321 const char *digits = "0123456789abcdef";
322 int r,hex=0;
323 for( i = 0; i < 31; i++ )
324 {
325 r = g_rand_int(h) % 16;
326 mcookie[i] = digits[r];
327 if( r > 9 )
328 hex++;
329 }
330 if( (hex % 2) == 0 )
331 r = g_rand_int(h) % 10;
332 else
333 r = g_rand_int(h) % 5 + 10;
334 mcookie[31] = digits[r];
335 mcookie[32] = 0;
336#endif
337 g_rand_free(h);
338
339 authfile = g_key_file_get_string(config, "base", "authfile", 0);
340 if(!authfile)
341 {
342 mkdir("/var/run/lxdm",0700);
343 authfile = g_strdup("/var/run/lxdm/lxdm.auth");
344 }
345 tmp = g_strdup_printf("XAUTHORITY=%s", authfile);
346 putenv(tmp);
347 g_free(tmp);
348 remove(authfile);
349#if HAVE_LIBXAU
350 FILE *fp=fopen(authfile,"wb");
351 if(fp)
352 {
353 static char xau_address[80];
354 static char xau_number[16];
355 static char xau_name[]="MIT-MAGIC-COOKIE-1";
356 struct utsname uts;
357 uname(&uts);
358 sprintf(xau_address, "%s", uts.nodename);
359 strcpy(xau_number,getenv("DISPLAY")+1); // DISPLAY always exist at lxdm
360 x_auth.family = FamilyLocal;
361 x_auth.address = xau_address;
362 x_auth.number = xau_number;
363 x_auth.name = xau_name;
364 x_auth.address_length = strlen(xau_address);
365 x_auth.number_length = strlen(xau_number);
366 x_auth.name_length = strlen(xau_name);
367 x_auth.data = mcookie;
368 x_auth.data_length = 16;
369 XauWriteAuth(fp,&x_auth);
370 fclose(fp);
371 }
372#else
373 tmp = g_strdup_printf("xauth -q -f %s add %s . %s",
374 authfile, getenv("DISPLAY"), mcookie);
375 system(tmp);
376 g_free(tmp);
377
378#endif
379 g_free(authfile);
eea1c851
AL
380}
381
382void create_client_auth(char *home)
383{
faee902e
AL
384 char *authfile;
385
386 if( getuid() == 0 ) /* root don't need it */
387 return;
388
389 authfile = g_strdup_printf("%s/.Xauthority", home);
390 remove(authfile);
391#if HAVE_LIBXAU
392 FILE *fp=fopen(authfile,"wb");
393 if(fp)
394 {
395 XauWriteAuth(fp,&x_auth);
396 fclose(fp);
397 }
398#else
399 char *tmp = g_strdup_printf("xauth -q -f %s add %s . %s",
400 authfile, getenv("DISPLAY"), mcookie);
401 system(tmp);
402 g_free(tmp);
403#endif
404 g_free(authfile);
405}
406#endif
eea1c851 407
faee902e
AL
408#if HAVE_LIBPAM
409static char *user_pass[2];
410
411static int do_conv(int num, const struct pam_message **msg,struct pam_response **resp, void *arg)
412{
413 int result = PAM_SUCCESS;
414 int i;
415 *resp = (struct pam_response *) calloc(num, sizeof(struct pam_response));
416 for(i=0;i<num;i++)
eea1c851 417 {
faee902e
AL
418 switch(msg[i]->msg_style){
419 case PAM_PROMPT_ECHO_ON:
420 resp[i]->resp=strdup(user_pass[0]);
421 break;
422 case PAM_PROMPT_ECHO_OFF:
423 resp[i]->resp=strdup(user_pass[1]);
424 break;
425 default:
426 break;
eea1c851
AL
427 }
428 }
faee902e 429 return result;
eea1c851
AL
430}
431
eea1c851 432static pam_handle_t *pamh;
faee902e
AL
433static struct pam_conv conv={.conv=do_conv,.appdata_ptr=user_pass};
434#endif
eea1c851 435
faee902e 436int lxdm_auth_user(char *user, char *pass, struct passwd **ppw)
eea1c851 437{
faee902e
AL
438 struct passwd *pw;
439 struct spwd *sp;
440 char *real;
441 char *enc;
442 if( !user )
443 return AUTH_ERROR;
444 if( !user[0] )
445 return AUTH_BAD_USER;
446 pw = getpwnam(user);
447 endpwent();
448 if( !pw )
449 return AUTH_BAD_USER;
450 if( !pass )
451 {
452 *ppw = pw;
453 return AUTH_SUCCESS;
454 }
455 sp = getspnam(user);
456 if( !sp )
457 return AUTH_FAIL;
458 endspent();
459 real = sp->sp_pwdp;
460 if( !real || !real[0] )
461 {
462 if( !pass[0] )
463 {
464 *ppw = pw;
465 return AUTH_SUCCESS;
466 }
467 else
468 return AUTH_FAIL;
469 }
470 enc = crypt(pass, real);
471 if( strcmp(real, enc) )
472 return AUTH_FAIL;
473 if( strstr(pw->pw_shell, "nologin") )
474 return AUTH_PRIV;
475 *ppw = pw;
476#if HAVE_LIBPAM
477 if(pamh) pam_end(pamh,0);
478 if(PAM_SUCCESS != pam_start("lxdm", pw->pw_name, &conv, &pamh))
479 pamh=NULL;
480 else
481 {
482 user_pass[0]=user;user_pass[1]=pass;
483 pam_authenticate(pamh,PAM_SILENT);
484 user_pass[0]=0;user_pass[1]=0;
485 }
486#endif
487 return AUTH_SUCCESS;
488}
489
490#if HAVE_LIBPAM
491void setup_pam_session(struct passwd *pw,char *session_name)
492{
493 int err;
494 char x[256];
495
496 if(!pamh && PAM_SUCCESS != pam_start("lxdm", pw->pw_name, &conv, &pamh))
497 {
498 pamh = NULL;
499 return;
500 }
501 if(!pamh) return;
502 sprintf(x, "tty%d", tty);
503 pam_set_item(pamh, PAM_TTY, x);
eea1c851 504#ifdef PAM_XDISPLAY
faee902e 505 pam_set_item( pamh, PAM_XDISPLAY, getenv("DISPLAY") );
eea1c851 506#endif
eea1c851 507
faee902e
AL
508 if(session_name && session_name[0])
509 {
510 char *env;
511 env = g_strdup_printf ("DESKTOP_SESSION=%s", session_name);
512 pam_putenv (pamh, env);
513 g_free (env);
514 }
515 err = pam_open_session(pamh, 0); /* FIXME pam session failed */
516 if( err != PAM_SUCCESS )
517 log_print( "pam open session error \"%s\"\n", pam_strerror(pamh, err) );
eea1c851
AL
518}
519
520void close_pam_session(void)
521{
faee902e
AL
522 int err;
523 if( !pamh ) return;
524 err = pam_close_session(pamh, 0);
525 pam_end(pamh, err);
526 pamh = NULL;
527}
528
529void append_pam_environ(char **env)
530{
531 int i,j,n;
532 char **penv;
eea1c851 533 if(!pamh) return;
faee902e
AL
534 penv=pam_getenvlist(pamh);
535 if(!penv) return;
536 for(i=0;penv[i]!=NULL;i++)
537 {
538 //printf("PAM %s\n",penv[i]);
539 n=strcspn(penv[i],"=")+1;
540 for(j=0;env[j]!=NULL;j++)
541 {
542 if(!strncmp(penv[i],env[j],n))
543 break;
544 if(env[j+1]==NULL)
545 {
546 env[j+1]=g_strdup(penv[i]);
547 env[j+2]=NULL;
548 break;
549 }
550 }
551 free(penv[i]);
552 }
553 free(penv);
eea1c851
AL
554}
555
556#endif
557
faee902e 558void switch_user(struct passwd *pw, char *run, char **env)
eea1c851 559{
faee902e
AL
560 int fd;
561
562 g_spawn_command_line_sync ("/etc/lxdm/PreLogin",NULL,NULL,NULL,NULL);
563
564 if( !pw || initgroups(pw->pw_name, pw->pw_gid) ||
565 setgid(pw->pw_gid) || setuid(pw->pw_uid) || setsid() == -1 )
566 exit(EXIT_FAILURE);
567 chdir(pw->pw_dir);
568 fd=open(".xsession-errors",O_WRONLY|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR);
569 if(fd!=-1)
570 {
571 dup2(fd,STDERR_FILENO);
572 close(fd);
573 }
574#ifndef DISABLE_XAUTH
575 create_client_auth(pw->pw_dir);
576#endif
577 g_spawn_command_line_async ("/etc/lxdm/PostLogin",NULL);
578 execle("/etc/lxdm/Xsession", "/etc/lxdm/Xsession", run, NULL, env);
579 exit(EXIT_FAILURE);
eea1c851
AL
580}
581
582void get_lock(void)
583{
faee902e
AL
584 FILE *fp;
585 char *lockfile;
586
587 lockfile = g_key_file_get_string(config, "base", "lock", 0);
588 if( !lockfile ) lockfile = g_strdup("/var/run/lxdm.pid");
589
590 fp = fopen(lockfile, "r");
591 if( fp )
592 {
593 int pid;
594 int ret;
595 ret = fscanf(fp, "%d", &pid);
596 fclose(fp);
597 if(ret == 1 && pid!=getpid())
eea1c851 598 {
faee902e
AL
599 if(kill(pid, 0) == 0 || (ret == -1 && errno == EPERM))
600 {
601 /* we should only quit if the pid running is lxdm */
602#ifdef __linux__
603 char path[64],buf[128];
604 sprintf(path,"/proc/%d/exe",pid);
605 ret=readlink(path,buf,128);
606 if(ret<128 && ret>0 && strstr(buf,"lxdm-binary"))
607 lxdm_quit_self(1);
608#else
609 lxdm_quit_self(1);
610#endif
611 }
eea1c851 612 }
faee902e
AL
613 }
614 fp = fopen(lockfile, "w");
615 if( !fp )
616 {
617 log_print("open lock file %s fail\n",lockfile);
618 lxdm_quit_self(0);
619 }
620 fprintf( fp, "%d", getpid() );
621 fclose(fp);
622 g_free(lockfile);
eea1c851
AL
623}
624
625void put_lock(void)
626{
faee902e
AL
627 FILE *fp;
628 char *lockfile;
629
630 lockfile = g_key_file_get_string(config, "base", "lock", 0);
631 if( !lockfile ) lockfile = g_strdup("/var/run/lxdm.pid");
632 fp = fopen(lockfile, "r");
633 if( fp )
634 {
635 int pid;
636 int ret;
637 ret = fscanf(fp, "%d", &pid);
638 fclose(fp);
639 if( ret == 1 && pid == getpid() )
640 remove(lockfile);
641 }
642 g_free(lockfile);
eea1c851
AL
643}
644
645void stop_pid(int pid)
646{
faee902e
AL
647 if( pid <= 0 ) return;
648 if( killpg(pid, SIGTERM) < 0 )
649 killpg(pid, SIGKILL);
650 if( kill(pid, 0) == 0 )
651 {
652 if( kill(pid, SIGTERM) )
653 kill(pid, SIGKILL);
654 while( 1 )
655 {
656 int wpid, status;
657 wpid = wait(&status);
658 if( pid == wpid ) break;
659 }
660 }
661 while( waitpid(-1, 0, WNOHANG) > 0 ) ;
eea1c851
AL
662}
663
faee902e 664static void on_xserver_stop(GPid pid, gint status, gpointer data)
eea1c851 665{
faee902e
AL
666 //log_print("xserver stop, restart. return status %x\n",status);
667 stop_pid(server);
668 server = -1;
669 lxdm_restart_self();
eea1c851
AL
670}
671
faee902e 672static void set_numlock(void)
eea1c851 673{
faee902e
AL
674 Display *dpy;
675 XkbDescPtr xkb;
676 unsigned int mask;
677 int on;
678 int i;
679 if(!g_key_file_has_key(config,"base","numlock",NULL))
680 return;
681 on=g_key_file_get_integer(config,"base","numlock",0);
682 dpy=gdk_x11_get_default_xdisplay();
683 if(!dpy) return;
684 xkb = XkbGetKeyboard( dpy, XkbAllComponentsMask, XkbUseCoreKbd );
685 if(!xkb) return;
686 if(!xkb->names)
687 {
688 XkbFreeKeyboard(xkb,0,True);
689 return;
690 }
691 for(i = 0; i < XkbNumVirtualMods; i++)
692 {
693 char *s=XGetAtomName( xkb->dpy, xkb->names->vmods[i]);
694 if(!s) continue;
695 if(strcmp(s,"NumLock")) continue;
696 XkbVirtualModsToReal( xkb, 1 << i, &mask );
eea1c851
AL
697 break;
698 }
faee902e
AL
699 XkbFreeKeyboard( xkb, 0, True );
700 XkbLockModifiers ( dpy, XkbUseCoreKbd, mask, (on?mask:0));
701}
702
703void startx(void)
704{
705 char *arg;
706 char **args;
707
708 if( !getenv("DISPLAY") )
709 putenv("DISPLAY=:0");
710
711#ifndef DISABLE_XAUTH
712 create_server_auth();
713#endif
714
715 arg = g_key_file_get_string(config, "server", "arg", 0);
716 if( !arg ) arg = g_strdup("/usr/bin/X");
717 args = g_strsplit(arg, " ", -1);
718 g_free(arg);
719
720 server = vfork();
721
722 switch( server )
723 {
724 case 0:
725 execvp(args[0], args);
726 log_print("exec %s fail\n",args[0]);
727 lxdm_quit_self(0);
728 break;
729 case -1:
730 /* fatal error, should not restart self */
731 log_print("fork proc fail\n");
732 lxdm_quit_self(0);
733 break;
734 default:
735 break;
736 }
737 g_strfreev(args);
738 server_watch=g_child_watch_add(server, on_xserver_stop, 0);
eea1c851
AL
739}
740
741void exit_cb(void)
742{
faee902e
AL
743 if( child > 0 )
744 {
745 killpg(child, SIGHUP);
746 stop_pid(child);
747 child = -1;
748 }
749 ui_clean();
eea1c851 750#if HAVE_LIBPAM
faee902e 751 close_pam_session();
eea1c851 752#endif
faee902e
AL
753 if(server_watch>0)
754 {
755 g_source_remove(server_watch);
756 }
757 if( server > 0 )
758 {
759 stop_pid(server);
760 server = -1;
761 }
762 put_lock();
763 if( reason == 0 )
764 execlp(self, self, NULL);
765 if(reason!=1)
766 set_active_vt(old_tty);
eea1c851
AL
767}
768
769int CatchErrors(Display *dpy, XErrorEvent *ev)
770{
771 return 0;
772}
773
774void get_my_xid(void)
775{
faee902e
AL
776 Window dummy, parent;
777 Display *Dpy = gdk_x11_get_default_xdisplay();
778 Window Root = gdk_x11_get_default_root_xwindow();
779 XQueryTree(Dpy, Root, &dummy, &parent, &my_xid, &my_xid_n);
eea1c851
AL
780}
781
782int is_my_id(XID id)
783{
faee902e
AL
784 int i;
785 if( !my_xid )
786 return 0;
787 for( i = 0; i < my_xid_n; i++ )
788 if( id == my_xid[i] ) return 1;
789 return 0;
eea1c851
AL
790}
791
792void free_my_xid(void)
793{
faee902e
AL
794 XFree(my_xid);
795 my_xid = 0;
eea1c851
AL
796}
797
faee902e 798static void stop_clients(void)
eea1c851 799{
faee902e
AL
800 Window dummy, parent;
801 Window *children;
802 unsigned int nchildren;
803 unsigned int i;
804 Display *Dpy = gdk_x11_get_default_xdisplay();
805 Window Root = gdk_x11_get_default_root_xwindow();
806
807 XSync(Dpy, 0);
808 XSetErrorHandler(CatchErrors);
809
810 nchildren = 0;
811 XQueryTree(Dpy, Root, &dummy, &parent, &children, &nchildren);
812
813 for( i = 0; i < nchildren; i++ )
814 if( children[i] && !is_my_id(children[i]) )
815 XKillClient(Dpy, children[i]);
816
817 //printf("kill %d\n",i);
818 XFree( (char *)children );
819 XSync(Dpy, 0);
820 XSetErrorHandler(NULL);
821}
eea1c851 822
faee902e
AL
823static int get_run_level(void)
824{
825 int res=0;
826 struct utmp *ut,tmp;
827
828 setutent();
829 tmp.ut_type=RUN_LVL;
830 ut=getutid(&tmp);
831 if(!ut) return 0;
832 res=ut->ut_pid & 0xff;
833 endutent();
834 //log_print("runlevel %c\n",res);
835 return res;
836}
eea1c851 837
faee902e
AL
838static void on_session_stop(GPid pid, gint status, gpointer data)
839{
840 killpg(pid, SIGHUP);
841 stop_pid(pid);
842 child = -1;
843 int level;
844
845 if( server > 0 )
846 {
847 /* FIXME just work around lxde bug of focus can't set */
848 stop_clients();
849 free_my_xid();
850 }
851#if HAVE_LIBPAM
852 close_pam_session();
eea1c851 853#endif
faee902e
AL
854#if HAVE_LIBCK_CONNECTOR
855 if( ckc != NULL )
856 {
857 DBusError error;
858 dbus_error_init(&error);
859 ck_connector_close_session(ckc, &error);
860 unsetenv("XDG_SESSION_COOKIE");
861 }
862#endif
863 level=get_run_level();
864 if(level=='0' || level=='6')
865 {
866 if(level=='0')
867 g_spawn_command_line_sync("/etc/lxdm/PreShutdown",0,0,0,0);
868 else
869 g_spawn_command_line_sync("/etc/lxdm/PreReboot",0,0,0,0);
870 lxdm_quit_self(0);
871 }
872 ui_prepare();
873 g_spawn_command_line_async("/etc/lxdm/PostLogout",NULL);
eea1c851
AL
874}
875
faee902e 876static void replace_env(char** env, const char* name, const char* new_val)
eea1c851 877{
faee902e
AL
878 register char** penv;
879 for(penv = env; *penv; ++penv)
880 {
881 if(g_str_has_prefix(*penv, name))
882 {
883 g_free(*penv);
884 *penv = g_strconcat(name, new_val, NULL);
885 return;
886 }
887 }
888 *penv = g_strconcat(name, new_val, NULL);
889 *(penv + 1) = NULL;
890}
eea1c851 891
faee902e
AL
892gboolean lxdm_get_session_info(char *session,char **pname,char **pexec)
893{
894 char *name=NULL,*exec=NULL;
895 if(!session || !session[0])
eea1c851 896 {
faee902e
AL
897 name=g_key_file_get_string(config, "base", "session", 0);
898 if(!name && getenv("PREFERRED"))
899 name = g_strdup(getenv("PREFERRED"));
900 if(!session && getenv("DESKTOP"))
901 name = g_strdup(getenv("DESKTOP"));
902 if(!name) name=g_strdup("LXDE");
eea1c851 903 }
faee902e 904 else
eea1c851 905 {
faee902e
AL
906 char *p=strrchr(session,'.');
907 if(p && !strcmp(p,".desktop"))
908 {
909 GKeyFile *cfg=g_key_file_new();
910 if(!g_key_file_load_from_file(cfg,session,G_KEY_FILE_NONE,NULL))
911 {
912 g_key_file_free(cfg);
913 return FALSE;
914 }
915 name=g_key_file_get_string(cfg,"Desktop Entry","Name",NULL);
916 exec=g_key_file_get_string(cfg,"Desktop Entry","Exec",NULL);
917 g_key_file_free(cfg);
918 if(!name || !exec)
919 {
920 g_free(name);
921 g_free(exec);
922 return FALSE;
923 }
924 }
925 else
926 {
927 name=g_strdup(session);
928 }
eea1c851 929 }
faee902e 930 if(name && !exec)
eea1c851 931 {
faee902e
AL
932 if(!strcasecmp(name,"LXDE"))
933 exec = g_strdup("startlxde");
934 else if( !strcasecmp(name, "GNOME") )
935 exec = g_strdup("gnome-session");
936 else if( !strcasecmp(name, "KDE") )
937 exec = g_strdup("startkde");
938 else if( !strcasecmp(name, "XFCE") || !strcasecmp(name, "xfce4"))
939 exec = g_strdup("startxfce4");
940 else
941 exec=g_strdup(name);
eea1c851 942 }
faee902e
AL
943 if(pname) *pname=name;
944 if(pexec) *pexec=exec;
945 return TRUE;
eea1c851
AL
946}
947
faee902e 948void lxdm_do_login(struct passwd *pw, char *session, char *lang)
eea1c851 949{
faee902e
AL
950 char *session_name=0,*session_exec=0;
951 gboolean alloc_session=FALSE,alloc_lang=FALSE;
952 int pid;
953
954 if(!session ||!session[0] || !lang || !lang[0])
955 {
956 char *path=g_strdup_printf("%s/.dmrc",pw->pw_dir);
957 GKeyFile *dmrc=g_key_file_new();
958 g_key_file_load_from_file(dmrc,path,G_KEY_FILE_NONE,0);
959 g_free(path);
960 if(!session || !session[0])
961 {
962 session=g_key_file_get_string(dmrc,"Desktop","Session",NULL);
963 alloc_session=TRUE;
964 }
965 if(!lang || !lang[0])
966 {
967 lang=g_key_file_get_string(dmrc,"Desktop","Language",NULL);
968 alloc_lang=TRUE;
969 }
970 g_key_file_free(dmrc);
971 }
972
973 if(!lxdm_get_session_info(session,&session_name,&session_exec))
974 {
975 if(alloc_session)
976 g_free(session);
977 if(alloc_lang)
978 g_free(lang);
979 ui_prepare();
980 return;
981 }
982
983 if( pw->pw_shell[0] == '\0' )
984 {
985 setusershell();
986 strcpy( pw->pw_shell, getusershell() );
987 endusershell();
988 }
eea1c851 989#if HAVE_LIBPAM
faee902e 990 setup_pam_session(pw,session_name);
eea1c851
AL
991#endif
992#if HAVE_LIBCK_CONNECTOR
faee902e
AL
993 if( ckc != NULL )
994 {
995 DBusError error;
996 char x[256], *d, *n;
997 sprintf(x, "/dev/tty%d", tty);
998 dbus_error_init(&error);
999 d = x; n = getenv("DISPLAY");
1000 if( ck_connector_open_session_with_parameters(ckc, &error,
1001 "unix-user", &pw->pw_uid,
1002// disable this, follow the gdm way
1003 //"display-device", &d,
1004 "x11-display-device", &d,
1005 "x11-display", &n,
1006 NULL) )
1007 setenv("XDG_SESSION_COOKIE", ck_connector_get_cookie(ckc), 1);
1008 }
eea1c851 1009#endif
faee902e
AL
1010 get_my_xid();
1011 child = pid = fork();
1012 if( child == 0 )
1013 {
1014 char** env, *path;
1015 int n_env = g_strv_length(environ), i;
1016 /* copy all environment variables and override some of them */
1017 env = g_new(char*, n_env + 1 + 13);
1018 for( i = 0; i < n_env; ++i )
1019 env[i] = g_strdup(environ[i]);
1020 env[i] = NULL;
1021
1022 replace_env(env, "HOME=", pw->pw_dir);
1023 replace_env(env, "SHELL=", pw->pw_shell);
1024 replace_env(env, "USER=", pw->pw_name);
1025 replace_env(env, "LOGNAME=", pw->pw_name);
1026
1027 /* override $PATH if needed */
1028 path = g_key_file_get_string(config, "base", "path", 0);
1029 if( G_UNLIKELY(path) && path[0] ) /* if PATH is specified in config file */
1030 replace_env(env, "PATH=", path); /* override current $PATH with config value */
1031 else /* don't use the global env, they are bad for user */
1032 replace_env(env, "PATH=", "/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin"); /* set proper default */
1033 g_free(path);
1034 /* optionally override $LANG, $LC_MESSAGES, and $LANGUAGE */
1035 if( lang && lang[0] )
1036 {
1037 replace_env(env, "LANG=", lang);
1038 replace_env(env, "LC_MESSAGES=", lang);
1039 replace_env(env, "LANGUAGE=", lang);
1040 }
1041#if HAVE_LIBPAM
1042 append_pam_environ(env);
1043 pam_end(pamh,0);
1044#endif
1045 switch_user(pw, session_exec, env);
1046 lxdm_quit_self(4);
1047 }
1048 g_free(session_name);
1049 g_free(session_exec);
1050 if(alloc_session)
1051 g_free(session);
1052 if(alloc_lang)
1053 g_free(lang);
1054 g_child_watch_add(pid, on_session_stop, 0);
eea1c851
AL
1055}
1056
1057void lxdm_do_reboot(void)
1058{
faee902e
AL
1059 char *cmd;
1060 cmd = g_key_file_get_string(config, "cmd", "reboot", 0);
1061 if( !cmd ) cmd = g_strdup("reboot");
1062 g_spawn_command_line_sync("/etc/lxdm/PreReboot",0,0,0,0);
1063 g_spawn_command_line_async(cmd,0);
1064 g_free(cmd);
1065 lxdm_quit_self(0);
eea1c851
AL
1066}
1067
1068void lxdm_do_shutdown(void)
1069{
faee902e
AL
1070 char *cmd;
1071 cmd = g_key_file_get_string(config, "cmd", "shutdown", 0);
1072 if( !cmd ) cmd = g_strdup("shutdown -h now");
1073 g_spawn_command_line_sync("/etc/lxdm/PreReboot",0,0,0,0);
1074 reason = 1;
1075 g_spawn_command_line_async(cmd,0);
1076 g_free(cmd);
1077 lxdm_quit_self(0);
eea1c851
AL
1078}
1079
1080int lxdm_cur_session(void)
1081{
faee902e 1082 return child;
eea1c851
AL
1083}
1084
1085int lxdm_do_auto_login(void)
1086{
faee902e
AL
1087 struct passwd *pw;
1088 char *user;
1089 char *pass=NULL;
1090 int ret;
eea1c851 1091
eea1c851 1092
faee902e
AL
1093 user = g_key_file_get_string(config, "base", "autologin", 0);
1094 if( !user )
1095 return 0;
1096
1097#ifdef ENABLE_PASSWORD
1098 pass = g_key_file_get_string(config, "base", "password", 0);
eea1c851 1099#endif
faee902e
AL
1100 ret=lxdm_auth_user(user, pass, &pw);
1101 g_free(user);
1102 g_free(pass);
1103 if(ret!=AUTH_SUCCESS)
1104
1105 return 0;
1106 lxdm_do_login(pw, NULL, NULL);
1107 return 1;
1108}
eea1c851 1109
faee902e 1110static void log_sigsegv(void)
eea1c851 1111{
faee902e
AL
1112 void *array[40];
1113 size_t size;
1114 int fd;
eea1c851 1115
faee902e
AL
1116 fd=open("/var/log/lxdm.log",O_WRONLY|O_CREAT|O_APPEND,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
1117 if(fd==-1) return;
eea1c851 1118
faee902e
AL
1119 size=backtrace(array,40);
1120 backtrace_symbols_fd(array,size,fd);
eea1c851 1121
faee902e 1122 close(fd);
eea1c851
AL
1123}
1124
faee902e
AL
1125static void sig_handler(int sig)
1126{
1127 log_print("catch signal %d\n", sig);
1128 switch( sig )
1129 {
1130 case SIGTERM:
1131 case SIGINT:
1132 lxdm_quit_self(0);
1133 break;
1134 case SIGSEGV:
1135 log_sigsegv();
1136 lxdm_quit_self(0);
1137 break;
1138 default:
1139 break;
1140 }
1141}
eea1c851 1142
faee902e 1143void set_signal(void)
eea1c851 1144{
faee902e
AL
1145 signal(SIGQUIT, sig_handler);
1146 signal(SIGTERM, sig_handler);
1147 signal(SIGKILL, sig_handler);
1148 signal(SIGINT, sig_handler);
1149 signal(SIGHUP, sig_handler);
1150 signal(SIGPIPE, sig_handler);
1151 signal(SIGUSR1, sig_handler);
1152 signal(SIGALRM, sig_handler);
1153 signal(SIGSEGV, sig_handler);
1154}
eea1c851 1155
faee902e
AL
1156#if HAVE_LIBCK_CONNECTOR
1157void init_ck(void)
1158{
1159 ckc = ck_connector_new();
1160}
1161#endif
eea1c851 1162
faee902e
AL
1163int main(int arc, char *arg[])
1164{
1165 int tmp;
1166 int daemonmode = 0;
1167 int i;
1168
1169 if( getuid() != 0 )
1170 {
1171 printf("only root is allowed to use this program\n");
1172 exit(EXIT_FAILURE);
1173 }
1174
1175 for(i=1;i<arc;i++)
1176 {
1177 if(!strcmp(arg[i],"-d"))
1178 daemonmode=1;
1179 }
1180
1181 if( daemonmode )
1182 (void)daemon(1, 1);
1183
1184 self = arg[0];
1185
1186 config = g_key_file_new();
1187 g_key_file_load_from_file(config, CONFIG_FILE, G_KEY_FILE_NONE, NULL);
1188
1189 get_lock();
1190 atexit(exit_cb);
1191
1192 set_signal();
1193 lxdm_get_tty();
1194 startx();
1195
1196 for( tmp = 0; tmp < 200; tmp++ )
1197 {
1198 if( gdk_init_check(0, 0) )
1199 break;
1200 g_usleep(50 * 1000);
1201 }
1202 if( tmp >= 200 )
1203 exit(EXIT_FAILURE);
1204 set_numlock();
eea1c851
AL
1205
1206#if HAVE_LIBCK_CONNECTOR
faee902e 1207 init_ck();
eea1c851
AL
1208#endif
1209
faee902e 1210 lxdm_do_auto_login();
eea1c851 1211
faee902e 1212 ui_main();
eea1c851 1213
faee902e 1214 lxdm_restart_self();
eea1c851 1215
faee902e 1216 return 0;
eea1c851 1217}
faee902e 1218