Imported Upstream version 0.2.0
[debian/lxdm.git] / src / lxdm.c
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>
52 #include <X11/XKBlib.h>
53
54 #include <sys/vt.h>
55 #include <sys/ioctl.h>
56 #include <sys/stat.h>
57
58 #include <execinfo.h>
59
60 #include <utmp.h>
61
62 #if HAVE_LIBPAM
63 #include <security/pam_appl.h>
64 #endif
65
66 #if HAVE_LIBCK_CONNECTOR
67 #include <ck-connector.h>
68 #endif
69
70 #if HAVE_LIBXAU
71 #include <X11/Xauth.h>
72 #include <sys/utsname.h>
73 #endif
74
75 #include "lxdm.h"
76
77 GKeyFile *config;
78 static pid_t server;
79 static guint server_watch;
80 #if HAVE_LIBCK_CONNECTOR
81 static CkConnector *ckc;
82 #endif
83 static Window *my_xid;
84 static unsigned int my_xid_n;
85 static char *self;
86 static pid_t child;
87 static int reason;
88 static int old_tty=1,tty = 7;
89
90 #ifndef DISABLE_XAUTH
91 static char mcookie[33];
92 #endif
93
94 #if HAVE_LIBXAU
95 static Xauth x_auth;
96 #endif
97
98 static int get_active_vt(void)
99 {
100 int console_fd;
101 struct vt_stat console_state = { 0 };
102
103 console_fd = open("/dev/console", O_RDONLY | O_NOCTTY);
104
105 if( console_fd < 0 )
106 goto out;
107
108 if( ioctl(console_fd, VT_GETSTATE, &console_state) < 0 )
109 goto out;
110
111 out:
112 if( console_fd >= 0 )
113 close(console_fd);
114
115 return console_state.v_active;
116 }
117
118 static 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);
128 }
129
130 static gboolean plymouth_is_running(void)
131 {
132 int status;
133 gboolean res;
134
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
140 static void plymouth_quit_with_transition(void)
141 {
142 g_spawn_command_line_sync("/bin/plymouth quit --retain-splash",NULL,NULL,NULL,NULL);
143 }
144
145 static void plymouth_quit_without_transition(void)
146 {
147 g_spawn_command_line_sync("/bin/plymouth quit --retain-splash",NULL,NULL,NULL,NULL);
148 }
149
150 static void plymouth_prepare_transition(void)
151 {
152 g_spawn_command_line_sync ("/bin/plymouth deactivate",NULL,NULL,NULL,NULL);
153 }
154
155 void lxdm_get_tty(void)
156 {
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();
194 }
195 }
196 else
197 {
198 if(plymouth) /* set tty and plymouth running */
199 plymouth_quit_without_transition();
200 }
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);
215 }
216
217 void lxdm_restart_self(void)
218 {
219 reason = 0;
220 exit(0);
221 }
222
223 void lxdm_quit_self(int code)
224 {
225 reason = (code?code:255);
226 exit(0);
227 }
228
229 void log_print(char *fmt, ...)
230 {
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);
239 }
240
241 GSList *do_scan_xsessions(void)
242 {
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;
291 }
292
293 void free_xsessions(GSList *l)
294 {
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);
306 }
307
308 #ifndef DISABLE_XAUTH
309 void create_server_auth(void)
310 {
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);
380 }
381
382 void create_client_auth(char *home)
383 {
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
407
408 #if HAVE_LIBPAM
409 static char *user_pass[2];
410
411 static 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++)
417 {
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;
427 }
428 }
429 return result;
430 }
431
432 static pam_handle_t *pamh;
433 static struct pam_conv conv={.conv=do_conv,.appdata_ptr=user_pass};
434 #endif
435
436 int lxdm_auth_user(char *user, char *pass, struct passwd **ppw)
437 {
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
491 void 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);
504 #ifdef PAM_XDISPLAY
505 pam_set_item( pamh, PAM_XDISPLAY, getenv("DISPLAY") );
506 #endif
507
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) );
518 }
519
520 void close_pam_session(void)
521 {
522 int err;
523 if( !pamh ) return;
524 err = pam_close_session(pamh, 0);
525 pam_end(pamh, err);
526 pamh = NULL;
527 }
528
529 void append_pam_environ(char **env)
530 {
531 int i,j,n;
532 char **penv;
533 if(!pamh) return;
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);
554 }
555
556 #endif
557
558 void switch_user(struct passwd *pw, char *run, char **env)
559 {
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);
580 }
581
582 void get_lock(void)
583 {
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())
598 {
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 }
612 }
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);
623 }
624
625 void put_lock(void)
626 {
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);
643 }
644
645 void stop_pid(int pid)
646 {
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 ) ;
662 }
663
664 static void on_xserver_stop(GPid pid, gint status, gpointer data)
665 {
666 //log_print("xserver stop, restart. return status %x\n",status);
667 stop_pid(server);
668 server = -1;
669 lxdm_restart_self();
670 }
671
672 static void set_numlock(void)
673 {
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 );
697 break;
698 }
699 XkbFreeKeyboard( xkb, 0, True );
700 XkbLockModifiers ( dpy, XkbUseCoreKbd, mask, (on?mask:0));
701 }
702
703 void 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);
739 }
740
741 void exit_cb(void)
742 {
743 if( child > 0 )
744 {
745 killpg(child, SIGHUP);
746 stop_pid(child);
747 child = -1;
748 }
749 ui_clean();
750 #if HAVE_LIBPAM
751 close_pam_session();
752 #endif
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);
767 }
768
769 int CatchErrors(Display *dpy, XErrorEvent *ev)
770 {
771 return 0;
772 }
773
774 void get_my_xid(void)
775 {
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);
780 }
781
782 int is_my_id(XID id)
783 {
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;
790 }
791
792 void free_my_xid(void)
793 {
794 XFree(my_xid);
795 my_xid = 0;
796 }
797
798 static void stop_clients(void)
799 {
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 }
822
823 static 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 }
837
838 static 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();
853 #endif
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);
874 }
875
876 static void replace_env(char** env, const char* name, const char* new_val)
877 {
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 }
891
892 gboolean lxdm_get_session_info(char *session,char **pname,char **pexec)
893 {
894 char *name=NULL,*exec=NULL;
895 if(!session || !session[0])
896 {
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");
903 }
904 else
905 {
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 }
929 }
930 if(name && !exec)
931 {
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);
942 }
943 if(pname) *pname=name;
944 if(pexec) *pexec=exec;
945 return TRUE;
946 }
947
948 void lxdm_do_login(struct passwd *pw, char *session, char *lang)
949 {
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 }
989 #if HAVE_LIBPAM
990 setup_pam_session(pw,session_name);
991 #endif
992 #if HAVE_LIBCK_CONNECTOR
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 }
1009 #endif
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);
1055 }
1056
1057 void lxdm_do_reboot(void)
1058 {
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);
1066 }
1067
1068 void lxdm_do_shutdown(void)
1069 {
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);
1078 }
1079
1080 int lxdm_cur_session(void)
1081 {
1082 return child;
1083 }
1084
1085 int lxdm_do_auto_login(void)
1086 {
1087 struct passwd *pw;
1088 char *user;
1089 char *pass=NULL;
1090 int ret;
1091
1092
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);
1099 #endif
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 }
1109
1110 static void log_sigsegv(void)
1111 {
1112 void *array[40];
1113 size_t size;
1114 int fd;
1115
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;
1118
1119 size=backtrace(array,40);
1120 backtrace_symbols_fd(array,size,fd);
1121
1122 close(fd);
1123 }
1124
1125 static 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 }
1142
1143 void set_signal(void)
1144 {
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 }
1155
1156 #if HAVE_LIBCK_CONNECTOR
1157 void init_ck(void)
1158 {
1159 ckc = ck_connector_new();
1160 }
1161 #endif
1162
1163 int 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();
1205
1206 #if HAVE_LIBCK_CONNECTOR
1207 init_ck();
1208 #endif
1209
1210 lxdm_do_auto_login();
1211
1212 ui_main();
1213
1214 lxdm_restart_self();
1215
1216 return 0;
1217 }
1218