Initial import.
[profile/ivi/uxlaunch.git] / src / user.c
1 /*
2  * This file is part of uxlaunch
3  *
4  * (C) Copyright 2009 Intel Corporation
5  * Authors:
6  *     Auke Kok <auke@linux.intel.com>
7  *     Arjan van de Ven <arjan@linux.intel.com>
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; version 2
12  * of the License.
13  */
14
15 #define _GNU_SOURCE
16 #include <sys/types.h>
17 #include <unistd.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <dirent.h>
22 #include <sys/types.h>
23 #include <pwd.h>
24 #include <grp.h>
25 #include <sys/stat.h>
26 #include <sys/types.h>
27
28
29 #include "uxlaunch.h"
30
31 #include <X11/Xauth.h>
32
33 int uid;
34 struct passwd *pass;
35
36 char user_xauth_path[PATH_MAX];
37
38 static void do_env(void)
39 {
40         char buf[PATH_MAX];
41         FILE *file;
42
43         d_in();
44
45         /* start with a clean environ */
46         clearenv();
47
48         setenv("USER", pass->pw_name, 1);
49         setenv("LOGNAME", pass->pw_name, 1);
50         setenv("HOME", pass->pw_dir, 1);
51         setenv("SHELL", pass->pw_shell, 1);
52         snprintf(buf, PATH_MAX, "/var/spool/mail/%s", pass->pw_name);
53         setenv("MAIL", buf, 1);
54         setenv("DISPLAY", displayname, 1);
55         snprintf(buf, PATH_MAX, "/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:%s/bin", pass->pw_dir);
56         setenv("PATH", buf, 1);
57         snprintf(user_xauth_path, PATH_MAX, "%s/.Xauthority", pass->pw_dir);
58         setenv("XAUTHORITY", user_xauth_path, 1);
59
60         file = popen("/bin/bash -l -c export", "r");
61         if (!file)
62                 return;
63
64         while (!feof(file)) {
65                 char *c;
66                 memset(buf, 0, sizeof(buf));
67                 if (fgets(buf, sizeof(buf) - 1, file) == NULL)
68                                 break;
69                 c = strchr(buf, '\n');
70
71                 if (strlen(buf) < 12)
72                         continue;
73                 if (c)
74                         *c = 0;
75
76                 if (strstr(buf, "PWD"))
77                         continue;
78 //              if (strstr(buf, "DISPLAY"))
79 //                      continue;
80
81                 c = strchr(buf, '=');
82                 if (c) {
83                         char *c2;
84                         *c = 0;
85                         c++;
86                         if (*c == '"') c++;
87                         c2 = strchr(c, '"');
88                         if (c2)
89                                 *c2 = 0;
90                         dprintf("Setting %s to %s\n", &buf[11], c);
91                         setenv(&buf[11], c, 1);
92                 }
93
94         }
95
96         pclose(file);
97         d_out();
98 }
99
100 #define BACKLIGHT_CLASS "/sys/class/backlight"
101 #define BACKLIGHT_FILE "brightness"
102
103 static void set_backlight_driver_perms(const char *backlight_dir_path)
104 {
105         char backlight_file_path[PATH_MAX];
106         int ret;
107
108         d_in();
109
110         snprintf(backlight_file_path, sizeof(backlight_file_path),
111                  "%s/%s", backlight_dir_path, BACKLIGHT_FILE);
112
113         ret = chown(backlight_dir_path, pass->pw_uid, pass->pw_gid);
114         if (ret)
115                 lprintf("Failed to set \"%s\" ownership", backlight_dir_path);
116
117         ret = chown(backlight_file_path, pass->pw_uid, pass->pw_gid);
118         if (ret)
119                 lprintf("Failed to set \"%s\" ownership", backlight_file_path);
120
121         d_out();
122 }
123
124 static void set_backlight_perms(const char *backlight_class)
125 {
126         DIR *dir;
127         struct dirent *entry;
128         char backlight_dir_path[PATH_MAX];
129
130         d_in();
131
132         dir = opendir(backlight_class);
133         if (dir) {
134                 while (NULL != (entry = readdir (dir))) {
135                         if (entry->d_name && entry->d_name[0] != '.' &&
136                             entry->d_type == DT_LNK) {
137
138                                 snprintf(backlight_dir_path,
139                                          sizeof (backlight_dir_path),
140                                          "%s/%s", backlight_class,
141                                           entry->d_name);
142                                 set_backlight_driver_perms(backlight_dir_path);
143                         }
144                 }
145                 closedir (dir);
146         } else {
147                 lprintf ("Failed to opendir(\"%s\")", backlight_class);
148         }
149
150         d_out();
151 }
152
153 /*
154  * Change from root (as we started) to the target user.
155  * Steps
156  * 1) setuid/getgid
157  * 2) env variables: HOME, MAIL, LOGNAME, USER, SHELL, DISPLAY and PATH
158  * 3) chdir(/home/foo);
159  */
160 void switch_to_user(void)
161 {
162         FILE *fp;
163         char fn[PATH_MAX];
164         int ret;
165
166         d_in();
167
168         initgroups(pass->pw_name, pass->pw_gid);
169
170         /* make sure that the user owns /dev/ttyX */
171         ret = chown(displaydev, pass->pw_uid, pass->pw_gid);
172         if (ret)
173                 lprintf("Failed to fix /dev/tty permission");
174
175         /* make sure the user owns the X backlight devices */
176         set_backlight_perms (BACKLIGHT_CLASS);
177
178         if (!((setgid(pass->pw_gid) == 0) && (setuid(pass->pw_uid) == 0))) {
179                 lprintf("Fatal: Unable to setgid()/setuid()\n");
180                 exit(EXIT_FAILURE);
181         }
182
183         if (access(pass->pw_dir, R_OK || W_OK || X_OK) != 0) {
184                 lprintf("Fatal: \"%s\" has incompatible permissions", pass->pw_dir);
185                 exit(EXIT_FAILURE);
186         }
187
188         if (setpgid(0, getpgid(getppid())) == -1)
189                 lprintf("Unable to setpgid()");
190         if (setsid() == -1)
191                 lprintf("Unable to setsid()");
192
193         do_env();
194
195         set_i18n();
196
197         ret = chdir(pass->pw_dir);
198
199         fp = fopen(user_xauth_path, "w");
200         if (fp) {
201                 if (XauWriteAuth(fp, &x_auth) != 1)
202                         lprintf("Unable to write .Xauthority");
203                 fclose(fp);
204         }
205
206         /* redirect further IO to .xsession-errors */
207         snprintf(fn, PATH_MAX, "%s/.xsession-errors", pass->pw_dir);
208         fp = fopen(fn, "w");
209         if (fp) {
210                 fclose(fp);
211                 /* xserver.c already truncates this file, so append */
212                 fp = freopen(fn, "a", stdout);
213                 fp = freopen(fn, "a", stderr);
214         } else {
215                 lprintf("Unable to open \"%s\n\" for writing", fn);
216         }
217
218         d_out();
219 }
220
221 static char *scim_languages[] = { "zh_", "ja_", "ko_", "lo_", "th_" };
222
223 void setup_user_environment (void)
224 {
225         unsigned int i;
226         char buf[PATH_MAX];
227         const char *lang = getenv ("LANG");
228
229         d_in();
230
231         for (i = 0; lang && i < sizeof(scim_languages) / sizeof(scim_languages[0]); i++) {
232                 if (strstr(lang, scim_languages[i])) {
233                         setenv("GTK_IM_MODULE", "scim-bridge", 0);
234                         setenv("CLUTTER_IM_MODULE","scim-bridge", 0);
235                 }
236         }
237
238         /* setup misc. user directories and variables */
239         snprintf(buf, PATH_MAX, "%s/.cache", pass->pw_dir);
240         mkdir(buf, 0700);
241         setenv("XDG_CACHE_HOME", buf, 0);
242         snprintf(buf, PATH_MAX, "%s/.config", pass->pw_dir);
243         setenv("XDG_CONFIG_HOME", buf, 0);
244         setenv("OOO_FORCE_DESKTOP","gnome", 0);
245         setenv("LIBC_FATAL_STDERR_", "1", 0);
246
247         d_out();
248 }
249
250 void set_i18n(void)
251 {
252         FILE *f;
253         char path[PATH_MAX];
254         char buf[256];
255         char *key;
256         char *val;
257
258         d_in();
259
260         /*
261          * /etc/sysconfig/i18n contains shell code that sets
262          * various i18n options in environment, typically:
263          * LANG, SYSFONT
264          */
265         snprintf(path, PATH_MAX, "%s/.config/i18n", pass->pw_dir);
266         f = fopen(path, "r");
267         if (f)
268                 goto parse;
269         dprintf("Unable to open ~/.config/i18n, trying /etc/sysconfig/i18n");
270         f = fopen("/etc/sysconfig/i18n", "r");
271         if (f)
272                 goto parse;
273         d_out();
274         return;
275
276 parse:
277         while (fgets(buf, 256, f) != NULL) {
278                 char *c;
279
280                 c = strchr(buf, '\n');
281                 if (c) *c = 0; /* remove trailing \n */
282                 if (buf[0] == '#')
283                         continue; /* skip comments */
284
285                 key = strtok(buf, "=");
286                 if (!key)
287                         continue;
288                 val = strtok(NULL, "=\""); /* note \" */
289                 if (!val)
290                         continue;
291
292                 /* grab the stuff we need, avoiding comments
293                  * and other user stuff we don't care for now */
294                 if (!strcmp(key, "LANG"))
295                         setenv(key, val, 1);
296                 if (!strcmp(key, "SYSFONT"))
297                         setenv(key, val, 1);
298         }
299         fclose(f);
300
301         d_out();
302 }