1 /* Copyright (c) 2008 Apple Inc.
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:
11 * The above copyright notice and this permission notice shall be
12 * included in all copies or substantial portions of the Software.
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.
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.
29 #include <mach/mach.h>
30 #include <mach/mach_error.h>
31 #include <servers/bootstrap.h>
34 #include <sys/types.h>
46 #include "privileged_startx.h"
47 #include "privileged_startxServer.h"
50 union __RequestUnion__privileged_startx_subsystem req;
51 union __ReplyUnion__privileged_startx_subsystem rep;
54 /* globals to trigger idle exit */
55 #define DEFAULT_IDLE_TIMEOUT 60 /* 60 second timeout, then the server exits */
60 struct timeval lastmsg;
63 struct idle_globals idle_globals;
66 #define SCRIPTDIR="/usr/X11/lib/X11/xinit/privileged_startx.d"
69 /* Default script dir */
70 const char *script_dir = SCRIPTDIR;
72 static void* idle_thread(void* param __attribute__((unused)));
74 int server_main(const char *dir) {
75 mach_msg_size_t mxmsgsz = sizeof(union MaxMsgSize) + MAX_TRAILER_SIZE;
78 long idle_timeout = DEFAULT_IDLE_TIMEOUT;
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");
89 tmv = launch_data_dict_lookup(config, LAUNCH_JOBKEY_TIMEOUT);
91 idle_timeout = launch_data_get_integer(tmv);
92 asl_log(NULL, NULL, ASL_LEVEL_DEBUG,
93 "idle timeout set: %ld seconds", idle_timeout);
98 asl_log(NULL, NULL, ASL_LEVEL_DEBUG,
99 "script directory set: %s", script_dir);
103 svc = launch_data_dict_lookup(config, LAUNCH_JOBKEY_MACHSERVICES);
105 asl_log(NULL, NULL, ASL_LEVEL_ERR, "no mach services");
109 svc = launch_data_dict_lookup(svc, BOOTSTRAP_NAME);
111 asl_log(NULL, NULL, ASL_LEVEL_ERR, "no mach service: %s",
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",
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));
132 /* spawn a thread to monitor our idle timeout */
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);
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));
150 static int ftscmp(const FTSENT **a, const FTSENT **b) {
151 return strcmp((**a).fts_name, (**b).fts_name);
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];
162 const char * path_argv[2] = {script_dir, NULL};
164 /* Store that we were called, so the idle timer will reset */
165 gettimeofday(&idle_globals.lastmsg, NULL);
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);
170 asl_log(NULL, NULL, ASL_LEVEL_ERR,
171 "do_privileged_startx: fts_open(%s): %s\n",
172 script_dir, strerror(errno));
177 ftsent = fts_read(ftsp);
179 asl_log(NULL, NULL, ASL_LEVEL_ERR,
180 "do_privileged_startx: fts_read(): %s\n", strerror(errno));
185 /* Get a list of the files in this directory */
186 ftsent = fts_children(ftsp, 0);
188 asl_log(NULL, NULL, ASL_LEVEL_ERR,
189 "do_privileged_startx: fts_children(): %s\n", strerror(errno));
194 /* Setup the buffer to have the path to the script dir */
195 strncpy(fn_buf, script_dir, PATH_MAX-1);
197 s = strrchr(fn_buf, 0);
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)) {
206 /* Complete the full path filename in fn_buf */
207 strcpy(s, ftsent->fts_name);
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",
215 retval = KERN_FAILURE;
224 kern_return_t do_idle_exit(mach_port_t test_port __attribute__((unused))) {
226 gettimeofday(&now, NULL);
228 long delta = now.tv_sec - idle_globals.lastmsg.tv_sec;
229 if (delta >= idle_globals.timeout) {
236 static void *idle_thread(void* param __attribute__((unused))) {
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);
245 /* timeout has elapsed, attempt to idle exit */
246 idle_exit(idle_globals.mp);