initial commit
[profile/ivi/xorg-x11-xinit.git] / privileged_startx / server.c
1 /* Copyright (c) 2008 Apple Inc.
2  *
3  * Permission is hereby granted, free of charge, to any person
4  * obtaining a copy of this software and associated documentation files
5  * (the "Software"), to deal in the Software without restriction,
6  * including without limitation the rights to use, copy, modify, merge,
7  * publish, distribute, sublicense, and/or sell copies of the Software,
8  * and to permit persons to whom the Software is furnished to do so,
9  * subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be
12  * included in all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17  * NONINFRINGEMENT.  IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT
18  * HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  *
23  * Except as contained in this notice, the name(s) of the above
24  * copyright holders shall not be used in advertising or otherwise to
25  * promote the sale, use or other dealings in this Software without
26  * prior written authorization.
27  */
28
29 #include <mach/mach.h>
30 #include <mach/mach_error.h>
31 #include <servers/bootstrap.h>
32 #include <unistd.h>
33 #include <stdio.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <fts.h>
37 #include <limits.h>
38 #include <stdlib.h>
39 #include <stdbool.h>
40 #include <sys/time.h>
41 #include <launch.h>
42 #include <asl.h>
43 #include <pthread.h>
44 #include <errno.h>
45
46 #include "privileged_startx.h"
47 #include "privileged_startxServer.h"
48
49 union MaxMsgSize {
50     union __RequestUnion__privileged_startx_subsystem req;
51     union __ReplyUnion__privileged_startx_subsystem rep; 
52 };
53
54 /* globals to trigger idle exit */
55 #define DEFAULT_IDLE_TIMEOUT 60 /* 60 second timeout, then the server exits */
56
57 struct idle_globals {
58         mach_port_t      mp;
59         long    timeout;
60         struct timeval   lastmsg;
61 };
62
63 struct idle_globals idle_globals;
64
65 #ifndef SCRIPTDIR
66 #define SCRIPTDIR="/usr/X11/lib/X11/xinit/privileged_startx.d"
67 #endif
68
69 /* Default script dir */
70 const char *script_dir = SCRIPTDIR;
71
72 static void* idle_thread(void* param __attribute__((unused)));
73
74 int server_main(const char *dir) {
75     mach_msg_size_t mxmsgsz = sizeof(union MaxMsgSize) + MAX_TRAILER_SIZE;
76     mach_port_t mp;
77     kern_return_t kr;
78     long idle_timeout = DEFAULT_IDLE_TIMEOUT;
79
80     launch_data_t config = NULL, checkin = NULL;
81     checkin = launch_data_new_string(LAUNCH_KEY_CHECKIN);
82     config = launch_msg(checkin);
83     if (!config || launch_data_get_type(config) == LAUNCH_DATA_ERRNO) {
84         asl_log(NULL, NULL, ASL_LEVEL_ERR, "launchd checkin failed");
85         exit(EXIT_FAILURE);
86     }
87
88     launch_data_t tmv;
89     tmv = launch_data_dict_lookup(config, LAUNCH_JOBKEY_TIMEOUT);
90     if (tmv) {
91         idle_timeout = launch_data_get_integer(tmv);
92         asl_log(NULL, NULL, ASL_LEVEL_DEBUG,
93                 "idle timeout set: %ld seconds", idle_timeout);
94     }
95
96     if(dir) {
97         script_dir = dir;
98         asl_log(NULL, NULL, ASL_LEVEL_DEBUG,
99                 "script directory set: %s", script_dir);
100     }
101
102     launch_data_t svc;
103     svc = launch_data_dict_lookup(config, LAUNCH_JOBKEY_MACHSERVICES);
104     if (!svc) {
105         asl_log(NULL, NULL, ASL_LEVEL_ERR, "no mach services");
106         exit(EXIT_FAILURE);
107     }
108
109     svc = launch_data_dict_lookup(svc, BOOTSTRAP_NAME);
110     if (!svc) {
111         asl_log(NULL, NULL, ASL_LEVEL_ERR, "no mach service: %s",
112                 BOOTSTRAP_NAME);
113         exit(EXIT_FAILURE);
114     }
115
116     mp = launch_data_get_machport(svc);
117     if (mp == MACH_PORT_NULL) {
118         asl_log(NULL, NULL, ASL_LEVEL_ERR, "NULL mach service: %s",
119                 BOOTSTRAP_NAME);
120         exit(EXIT_FAILURE);
121     }
122
123     /* insert a send right so we can send our idle exit message */
124     kr = mach_port_insert_right(mach_task_self(), mp, mp,
125                                 MACH_MSG_TYPE_MAKE_SEND);
126     if (kr != KERN_SUCCESS) {
127         asl_log(NULL, NULL, ASL_LEVEL_ERR, "send right failed: %s",
128                 mach_error_string(kr));
129         exit(EXIT_FAILURE);
130     }
131
132     /* spawn a thread to monitor our idle timeout */
133     pthread_t thread;
134     idle_globals.mp = mp;
135     idle_globals.timeout = idle_timeout;
136     gettimeofday(&idle_globals.lastmsg, NULL);
137     pthread_create(&thread, NULL, &idle_thread, NULL);
138
139     /* Main event loop */
140     kr = mach_msg_server(privileged_startx_server, mxmsgsz, mp, 0);
141     if (kr != KERN_SUCCESS) {
142         asl_log(NULL, NULL, ASL_LEVEL_ERR,
143                 "mach_msg_server(mp): %s\n", mach_error_string(kr));
144         exit(EXIT_FAILURE);
145     }
146
147     exit(EXIT_SUCCESS);
148 }
149
150 static int ftscmp(const FTSENT **a, const FTSENT **b) {
151     return strcmp((**a).fts_name, (**b).fts_name);
152 }
153
154 kern_return_t do_privileged_startx(mach_port_t test_port __attribute__((unused))) {
155     kern_return_t retval = KERN_SUCCESS;
156     char fn_buf[PATH_MAX + 1];
157     char *s;
158     int error_code;
159     FTS *ftsp;
160     FTSENT *ftsent;
161
162     const char * path_argv[2] = {script_dir, NULL};
163
164     /* Store that we were called, so the idle timer will reset */
165     gettimeofday(&idle_globals.lastmsg, NULL);
166
167     /* script_dir contains a set of files to run with root privs when X11 starts */
168     ftsp = fts_open(path_argv, FTS_PHYSICAL, ftscmp);
169     if(!ftsp) {
170         asl_log(NULL, NULL, ASL_LEVEL_ERR,
171                 "do_privileged_startx: fts_open(%s): %s\n",
172                 script_dir, strerror(errno));
173         return KERN_FAILURE;
174     }
175
176     /* Grab our dir */
177     ftsent = fts_read(ftsp);
178     if(!ftsent) {
179         asl_log(NULL, NULL, ASL_LEVEL_ERR,
180                 "do_privileged_startx: fts_read(): %s\n", strerror(errno));
181         fts_close(ftsp);
182         return KERN_FAILURE;
183     }
184
185     /* Get a list of the files in this directory */
186     ftsent = fts_children(ftsp, 0);
187     if(!ftsent) {
188         asl_log(NULL, NULL, ASL_LEVEL_ERR,
189                 "do_privileged_startx: fts_children(): %s\n", strerror(errno));
190         fts_close(ftsp);
191         return KERN_FAILURE;
192     }
193
194     /* Setup the buffer to have the path to the script dir */
195     strncpy(fn_buf, script_dir, PATH_MAX-1);
196     strcat(fn_buf, "/");
197     s = strrchr(fn_buf, 0);
198
199     /* Itterate over these files in alphabetical order */
200     for(; ftsent; ftsent = ftsent->fts_link) {
201         /* We only source regular files that are executable */
202         /* Note: This assumes we own them, which should always be the case */
203         if((ftsent->fts_statp->st_mode & S_IFREG) &&
204            (ftsent->fts_statp->st_mode & S_IXUSR)) {
205
206             /* Complete the full path filename in fn_buf */
207             strcpy(s, ftsent->fts_name);
208
209             /* Run it */
210             error_code = system(fn_buf);
211             if(error_code != 0) {
212                 asl_log(NULL, NULL, ASL_LEVEL_ERR,
213                         "do_privileged_startx: %s: exited with status %d\n",
214                         fn_buf, error_code);
215                 retval = KERN_FAILURE;
216             }
217         }
218     }
219
220     fts_close(ftsp);
221     return retval;
222 }
223
224 kern_return_t do_idle_exit(mach_port_t test_port __attribute__((unused))) {
225     struct timeval now;
226     gettimeofday(&now, NULL);
227
228     long delta = now.tv_sec - idle_globals.lastmsg.tv_sec;
229     if (delta >= idle_globals.timeout) {
230         exit(EXIT_SUCCESS);
231     }
232
233     return KERN_SUCCESS;
234 }
235
236 static void *idle_thread(void* param __attribute__((unused))) {
237     for(;;) {
238         struct timeval now;
239         gettimeofday(&now, NULL);
240         long delta = (now.tv_sec - idle_globals.lastmsg.tv_sec);
241         if (delta < idle_globals.timeout) {
242             /* sleep for remainder of timeout */
243             sleep(idle_globals.timeout - delta);
244         } else {
245             /* timeout has elapsed, attempt to idle exit */
246             idle_exit(idle_globals.mp);
247         }
248     }
249     return NULL;
250 }