server.c revision c81d8f5e
1/* Copyright (c) 2008-2011 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#ifdef HAVE_CONFIG_H 30# include "config.h" 31#endif 32 33#include <mach/mach.h> 34#include <mach/mach_error.h> 35#include <servers/bootstrap.h> 36#include <unistd.h> 37#include <stdio.h> 38#include <sys/types.h> 39#include <sys/stat.h> 40#include <fts.h> 41#include <limits.h> 42#include <stdlib.h> 43#include <stdbool.h> 44#include <sys/time.h> 45#include <launch.h> 46#include <asl.h> 47#include <errno.h> 48 49#include "console_redirect.h" 50 51#include "privileged_startx.h" 52#include "privileged_startxServer.h" 53 54union MaxMsgSize { 55 union __RequestUnion__privileged_startx_subsystem req; 56 union __ReplyUnion__privileged_startx_subsystem rep; 57}; 58 59#ifdef LAUNCH_JOBKEY_MACHSERVICES 60#include <pthread.h> 61static void* idle_thread(void* param __attribute__((unused))); 62 63/* globals to trigger idle exit */ 64#define DEFAULT_IDLE_TIMEOUT 60 /* 60 second timeout, then the server exits */ 65 66struct idle_globals { 67 mach_port_t mp; 68 long timeout; 69 struct timeval lastmsg; 70}; 71 72struct idle_globals idle_globals; 73#endif 74 75#ifndef SCRIPTDIR 76#define SCRIPTDIR="/usr/X11/lib/X11/xinit/privileged_startx.d" 77#endif 78 79/* Default script dir */ 80const char *script_dir = SCRIPTDIR; 81 82#ifndef LAUNCH_JOBKEY_MACHSERVICES 83static mach_port_t checkin_or_register(char *bname) { 84 kern_return_t kr; 85 mach_port_t mp; 86 87 /* If we're started by launchd or the old mach_init */ 88 kr = bootstrap_check_in(bootstrap_port, bname, &mp); 89 if (kr == KERN_SUCCESS) 90 return mp; 91 92 /* We probably were not started by launchd or the old mach_init */ 93 kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &mp); 94 if (kr != KERN_SUCCESS) { 95 asl_log(NULL, NULL, ASL_LEVEL_ERR, "mach_port_allocate(): %s", mach_error_string(kr)); 96 exit(EXIT_FAILURE); 97 } 98 99 kr = mach_port_insert_right(mach_task_self(), mp, mp, MACH_MSG_TYPE_MAKE_SEND); 100 if (kr != KERN_SUCCESS) { 101 asl_log(NULL, NULL, ASL_LEVEL_ERR, "mach_port_insert_right(): %s", mach_error_string(kr)); 102 exit(EXIT_FAILURE); 103 } 104 105 kr = bootstrap_register(bootstrap_port, bname, mp); 106 if (kr != KERN_SUCCESS) { 107 asl_log(NULL, NULL, ASL_LEVEL_ERR, "bootstrap_register(): %s", mach_error_string(kr)); 108 exit(EXIT_FAILURE); 109 } 110 111 return mp; 112} 113#endif 114 115int server_main(const char *dir) { 116 mach_msg_size_t mxmsgsz = sizeof(union MaxMsgSize) + MAX_TRAILER_SIZE; 117 mach_port_t mp; 118 kern_return_t kr; 119#ifdef LAUNCH_JOBKEY_MACHSERVICES 120 long idle_timeout = DEFAULT_IDLE_TIMEOUT; 121#endif 122 123 launch_data_t config = NULL, checkin = NULL, label = NULL; 124 const char *labelstr = BUNDLE_ID_PREFIX".privileged_startx"; 125 aslclient aslc; 126 127 checkin = launch_data_new_string(LAUNCH_KEY_CHECKIN); 128 config = launch_msg(checkin); 129 if (!config || launch_data_get_type(config) == LAUNCH_DATA_ERRNO) { 130 asl_log(NULL, NULL, ASL_LEVEL_ERR, "launchd checkin failed"); 131 exit(EXIT_FAILURE); 132 } 133 134 if(dir) { 135 script_dir = dir; 136 asl_log(NULL, NULL, ASL_LEVEL_DEBUG, 137 "script directory set: %s", script_dir); 138 } 139 140 label = launch_data_dict_lookup(config, LAUNCH_JOBKEY_LABEL); 141 if (label) { 142 labelstr = launch_data_get_string(label); 143 } 144 145 aslc = asl_open(labelstr, BUNDLE_ID_PREFIX, ASL_OPT_NO_DELAY); 146 xi_asl_capture_fd(aslc, NULL, ASL_LEVEL_INFO, STDOUT_FILENO); 147 xi_asl_capture_fd(aslc, NULL, ASL_LEVEL_NOTICE, STDERR_FILENO); 148 149#ifdef LAUNCH_JOBKEY_MACHSERVICES 150 launch_data_t tmv; 151 tmv = launch_data_dict_lookup(config, LAUNCH_JOBKEY_TIMEOUT); 152 if (tmv) { 153 idle_timeout = launch_data_get_integer(tmv); 154 asl_log(NULL, NULL, ASL_LEVEL_DEBUG, 155 "idle timeout set: %ld seconds", idle_timeout); 156 } 157 158 launch_data_t svc; 159 svc = launch_data_dict_lookup(config, LAUNCH_JOBKEY_MACHSERVICES); 160 if (!svc) { 161 asl_log(NULL, NULL, ASL_LEVEL_ERR, "no mach services"); 162 exit(EXIT_FAILURE); 163 } 164 165 svc = launch_data_dict_lookup(svc, BOOTSTRAP_NAME); 166 if (!svc) { 167 asl_log(NULL, NULL, ASL_LEVEL_ERR, "no mach service: %s", 168 BOOTSTRAP_NAME); 169 exit(EXIT_FAILURE); 170 } 171 172 mp = launch_data_get_machport(svc); 173#else 174 mp = checkin_or_register(BUNDLE_ID_PREFIX".privileged_startx"); 175#endif 176 177 if (mp == MACH_PORT_NULL) { 178 asl_log(NULL, NULL, ASL_LEVEL_ERR, "NULL mach service: %s", 179 BOOTSTRAP_NAME); 180 exit(EXIT_FAILURE); 181 } 182 183 /* insert a send right so we can send our idle exit message */ 184 kr = mach_port_insert_right(mach_task_self(), mp, mp, 185 MACH_MSG_TYPE_MAKE_SEND); 186 if (kr != KERN_SUCCESS) { 187 asl_log(NULL, NULL, ASL_LEVEL_ERR, "send right failed: %s", 188 mach_error_string(kr)); 189 exit(EXIT_FAILURE); 190 } 191 192#ifdef LAUNCH_JOBKEY_MACHSERVICES 193 /* spawn a thread to monitor our idle timeout */ 194 pthread_t thread; 195 idle_globals.mp = mp; 196 idle_globals.timeout = idle_timeout; 197 gettimeofday(&idle_globals.lastmsg, NULL); 198 pthread_create(&thread, NULL, &idle_thread, NULL); 199#endif 200 201 /* Main event loop */ 202 kr = mach_msg_server(privileged_startx_server, mxmsgsz, mp, 0); 203 if (kr != KERN_SUCCESS) { 204 asl_log(NULL, NULL, ASL_LEVEL_ERR, 205 "mach_msg_server(mp): %s", mach_error_string(kr)); 206 exit(EXIT_FAILURE); 207 } 208 209 exit(EXIT_SUCCESS); 210} 211 212static int ftscmp(const FTSENT **a, const FTSENT **b) { 213 return strcmp((**a).fts_name, (**b).fts_name); 214} 215 216kern_return_t do_privileged_startx(mach_port_t test_port __attribute__((unused))) { 217 kern_return_t retval = KERN_SUCCESS; 218 char fn_buf[PATH_MAX + 1]; 219 char *s; 220 int error_code; 221 FTS *ftsp; 222 FTSENT *ftsent; 223 224 const char * path_argv[2] = {script_dir, NULL}; 225 226#ifdef LAUNCH_JOBKEY_MACHSERVICES 227 /* Store that we were called, so the idle timer will reset */ 228 gettimeofday(&idle_globals.lastmsg, NULL); 229#endif 230 231 /* script_dir contains a set of files to run with root privs when X11 starts */ 232 ftsp = fts_open((char * const *)path_argv, FTS_PHYSICAL, ftscmp); 233 if(!ftsp) { 234 asl_log(NULL, NULL, ASL_LEVEL_ERR, 235 "do_privileged_startx: fts_open(%s): %s", 236 script_dir, strerror(errno)); 237 return KERN_FAILURE; 238 } 239 240 /* Grab our dir */ 241 ftsent = fts_read(ftsp); 242 if(!ftsent) { 243 asl_log(NULL, NULL, ASL_LEVEL_ERR, 244 "do_privileged_startx: fts_read(): %s", strerror(errno)); 245 fts_close(ftsp); 246 return KERN_FAILURE; 247 } 248 249 /* Get a list of the files in this directory */ 250 ftsent = fts_children(ftsp, 0); 251 if(!ftsent) { 252 asl_log(NULL, NULL, ASL_LEVEL_ERR, 253 "do_privileged_startx: fts_children(): %s", strerror(errno)); 254 fts_close(ftsp); 255 return KERN_FAILURE; 256 } 257 258 /* Setup the buffer to have the path to the script dir */ 259 strncpy(fn_buf, script_dir, PATH_MAX-1); 260 strcat(fn_buf, "/"); 261 s = strrchr(fn_buf, 0); 262 263 /* Itterate over these files in alphabetical order */ 264 for(; ftsent; ftsent = ftsent->fts_link) { 265 /* We only source regular files that are executable */ 266 /* Note: This assumes we own them, which should always be the case */ 267 if((ftsent->fts_statp->st_mode & S_IFREG) && 268 (ftsent->fts_statp->st_mode & S_IXUSR)) { 269 270 /* Complete the full path filename in fn_buf */ 271 strcpy(s, ftsent->fts_name); 272 273 /* Run it */ 274 error_code = system(fn_buf); 275 if(error_code != 0) { 276 asl_log(NULL, NULL, ASL_LEVEL_ERR, 277 "do_privileged_startx: %s: exited with status %d", 278 fn_buf, error_code); 279 retval = KERN_FAILURE; 280 } 281 } 282 } 283 284 fts_close(ftsp); 285 return retval; 286} 287 288kern_return_t do_idle_exit(mach_port_t test_port __attribute__((unused))) { 289#ifdef LAUNCH_JOBKEY_MACHSERVICES 290 struct timeval now; 291 gettimeofday(&now, NULL); 292 293 long delta = now.tv_sec - idle_globals.lastmsg.tv_sec; 294 if (delta >= idle_globals.timeout) { 295 exit(EXIT_SUCCESS); 296 } 297 298 return KERN_SUCCESS; 299#else 300 return KERN_FAILURE; 301#endif 302} 303 304#ifdef LAUNCH_JOBKEY_MACHSERVICES 305static void *idle_thread(void* param __attribute__((unused))) { 306 for(;;) { 307 struct timeval now; 308 gettimeofday(&now, NULL); 309 long delta = (now.tv_sec - idle_globals.lastmsg.tv_sec); 310 if (delta < idle_globals.timeout) { 311 /* sleep for remainder of timeout */ 312 sleep(idle_globals.timeout - delta); 313 } else { 314 /* timeout has elapsed, attempt to idle exit */ 315 idle_exit(idle_globals.mp); 316 } 317 } 318 return NULL; 319} 320#endif 321