Revision: 23574 http://trac.macosforge.org/projects/launchd/changeset/23574 Author: zarzycki@apple.com Date: 2008-03-27 14:18:33 -0700 (Thu, 27 Mar 2008) Log Message: ----------- <rdar://problem/5752398> Need a darwin clean/dirty API Modified Paths: -------------- trunk/launchd/src/Makefile.am trunk/launchd/src/Makefile.in trunk/launchd/src/launchd.plist.5 trunk/launchd/src/launchd_core_logic.c trunk/launchd/src/liblaunch_public.h trunk/launchd/src/libvproc.c trunk/launchd/src/libvproc_internal.h trunk/launchd/src/libvproc_private.h trunk/launchd/src/libvproc_public.h trunk/launchd/src/protocol_job.defs Modified: trunk/launchd/src/Makefile.am =================================================================== --- trunk/launchd/src/Makefile.am 2008-03-27 01:40:55 UTC (rev 23573) +++ trunk/launchd/src/Makefile.am 2008-03-27 21:18:33 UTC (rev 23574) @@ -1,5 +1,5 @@ CWARN = -Wall -Wextra -Waggregate-return -Wfloat-equal -Wshadow -Wpacked -Wmissing-prototypes -Wmissing-declarations -Werror # gcc4.2: -Wstrict-overflow=4 # -Wpadded -Wconversion -Wstrict-aliasing=2 -CTUNE = -fvisibility=hidden # gcc4.2: -fdiagnostics-show-option # -fstrict-aliasing +CTUNE = -fvisibility=hidden -freorder-blocks # gcc4.2: -fdiagnostics-show-option # -fstrict-aliasing CMISC = -isysroot $(SDKROOT) -F$(SDKROOT)/System/Library/PrivateFrameworks -D__MigTypeCheck=1 -Dmig_external=__private_extern__ -D_DARWIN_USE_64_BIT_INODE=1 AM_CFLAGS = $(CTUNE) $(CMISC) $(CWARN) AM_LDFLAGS = -Wl,-syslibroot,$(SDKROOT) @@ -57,7 +57,7 @@ SystemStarter_SOURCES = StartupItems.c IPC.c SystemStarter.c endif -launchd_CFLAGS = -mdynamic-no-pic $(AM_CFLAGS) -freorder-blocks +launchd_CFLAGS = -mdynamic-no-pic $(AM_CFLAGS) launchd_LDFLAGS = $(AM_LDFLAGS) -lbsm launchd_SOURCES = launchd.c launchd_core_logic.c launchd_unix_ipc.c protocol_vprocServer.c notifyServer.c launchd_internalUser.c launchd_internalServer.c job_replyUser.c launchd_runtime.c launchd_runtime_kill.c job_forwardUser.c mach_excServer.c Modified: trunk/launchd/src/Makefile.in =================================================================== --- trunk/launchd/src/Makefile.in 2008-03-27 01:40:55 UTC (rev 23573) +++ trunk/launchd/src/Makefile.in 2008-03-27 21:18:33 UTC (rev 23574) @@ -243,7 +243,7 @@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ CWARN = -Wall -Wextra -Waggregate-return -Wfloat-equal -Wshadow -Wpacked -Wmissing-prototypes -Wmissing-declarations -Werror # gcc4.2: -Wstrict-overflow=4 # -Wpadded -Wconversion -Wstrict-aliasing=2 -CTUNE = -fvisibility=hidden # gcc4.2: -fdiagnostics-show-option # -fstrict-aliasing +CTUNE = -fvisibility=hidden -freorder-blocks # gcc4.2: -fdiagnostics-show-option # -fstrict-aliasing CMISC = -isysroot $(SDKROOT) -F$(SDKROOT)/System/Library/PrivateFrameworks -D__MigTypeCheck=1 -Dmig_external=__private_extern__ -D_DARWIN_USE_64_BIT_INODE=1 AM_CFLAGS = $(CTUNE) $(CMISC) $(CWARN) AM_LDFLAGS = -Wl,-syslibroot,$(SDKROOT) @@ -262,7 +262,7 @@ @DO_EMBEDDED_MAGIC_FALSE@@LIBS_ONLY_FALSE@SystemStarter_CFLAGS = -mdynamic-no-pic $(AM_CFLAGS) @DO_EMBEDDED_MAGIC_FALSE@@LIBS_ONLY_FALSE@SystemStarter_LDFLAGS = $(AM_LDFLAGS) -framework CoreFoundation -framework IOKit @DO_EMBEDDED_MAGIC_FALSE@@LIBS_ONLY_FALSE@SystemStarter_SOURCES = StartupItems.c IPC.c SystemStarter.c -@LIBS_ONLY_FALSE@launchd_CFLAGS = -mdynamic-no-pic $(AM_CFLAGS) -freorder-blocks +@LIBS_ONLY_FALSE@launchd_CFLAGS = -mdynamic-no-pic $(AM_CFLAGS) @LIBS_ONLY_FALSE@launchd_LDFLAGS = $(AM_LDFLAGS) -lbsm @LIBS_ONLY_FALSE@launchd_SOURCES = launchd.c launchd_core_logic.c launchd_unix_ipc.c protocol_vprocServer.c notifyServer.c launchd_internalUser.c launchd_internalServer.c job_replyUser.c launchd_runtime.c launchd_runtime_kill.c job_forwardUser.c mach_excServer.c @LIBS_ONLY_FALSE@launchproxy_LDFLAGS = $(AM_LDFLAGS) $(LIBS_SECURITY) Modified: trunk/launchd/src/launchd.plist.5 =================================================================== --- trunk/launchd/src/launchd.plist.5 2008-03-27 01:40:55 UTC (rev 23573) +++ trunk/launchd/src/launchd.plist.5 2008-03-27 21:18:33 UTC (rev 23574) @@ -123,6 +123,16 @@ to use the .Xr glob 3 mechanism to update the program arguments before invocation. +.It Sy EnableTransactions <boolean> +This flag instructs +.Nm launchd +that the job promises to use +.Xr vproc_transaction_begin 3 +and +.Xr vproc_transaction_end 3 +to track outstanding transactions that need to be reconciled before the process can safely terminate. If no outstanding transactions are in progress, then +.Nm launchd +is free to send the SIGKILL signal. .It Sy OnDemand <boolean> This key was used in Mac OS X 10.4 to control whether a job was kept alive or not. The default was true. This key has been deprecated and replaced in Mac OS X 10.5 with the more powerful KeepAlive option. Modified: trunk/launchd/src/launchd_core_logic.c =================================================================== --- trunk/launchd/src/launchd_core_logic.c 2008-03-27 01:40:55 UTC (rev 23573) +++ trunk/launchd/src/launchd_core_logic.c 2008-03-27 21:18:33 UTC (rev 23574) @@ -50,6 +50,7 @@ #include <sys/ioctl.h> #include <sys/mount.h> #include <sys/pipe.h> +#include <sys/mman.h> #include <net/if.h> #include <netinet/in.h> #include <netinet/in_var.h> @@ -360,6 +361,7 @@ char *stdoutpath; char *stderrpath; char *alt_exc_handler; + struct vproc_shmem_s *shmem; struct machservice *lastlookup; unsigned int lastlookup_gennum; #if HAVE_SANDBOX @@ -384,14 +386,14 @@ uint64_t start_time; uint32_t min_run_time; uint32_t start_interval; - unsigned short checkedin:1, anonymous:1, debug:1, inetcompat:1, inetcompat_wait:1, + bool checkedin:1, anonymous:1, debug:1, inetcompat:1, inetcompat_wait:1, ondemand:1, session_create:1, low_pri_io:1, no_init_groups:1, priv_port_has_senders:1, importing_global_env:1, importing_hard_limits:1, setmask:1, legacy_mach_job:1, start_pending:1, globargv:1, wait4debugger:1, internal_exc_handler:1, stall_before_exec:1, only_once:1, currently_ignored:1, forced_peers_to_demand_mode:1, setnice:1, hopefully_exits_last:1, removal_pending:1, legacy_LS_job:1, sent_sigkill:1, debug_before_kill:1, weird_bootstrap:1, start_on_mount:1, per_user:1, hopefully_exits_first:1, deny_unknown_mslookups:1, unload_at_mig_return:1, abandon_pg:1, - poll_for_vfs_changes:1, deny_job_creation:1, __junk:11; + poll_for_vfs_changes:1, deny_job_creation:1, kill_via_shmem:1, sent_kill_via_shmem:1; mode_t mask; const char label[0]; }; @@ -561,19 +563,40 @@ INTERNAL_ABI void job_stop(job_t j) { + int32_t newval = 1; + if (unlikely(!j->p || j->anonymous)) { return; } - job_assumes(j, runtime_kill(j->p, SIGTERM) != -1); + if (j->kill_via_shmem) { + if (j->shmem) { + if (!j->sent_kill_via_shmem) { + j->shmem->vp_shmem_flags |= VPROC_SHMEM_EXITING; + newval = __sync_sub_and_fetch(&j->shmem->vp_shmem_transaction_cnt, 1); + j->sent_kill_via_shmem = true; + } else { + newval = j->shmem->vp_shmem_transaction_cnt; + } + } else { + newval = -1; + } + } + j->sent_sigterm_time = runtime_get_opaque_time(); - if (j->exit_timeout) { - job_assumes(j, kevent_mod((uintptr_t)&j->exit_timeout, EVFILT_TIMER, - EV_ADD|EV_ONESHOT, NOTE_SECONDS, j->exit_timeout, j) != -1); + if (newval < 0) { + job_kill(j); + } else { + job_assumes(j, runtime_kill(j->p, SIGTERM) != -1); + + if (j->exit_timeout) { + job_assumes(j, kevent_mod((uintptr_t)&j->exit_timeout, EVFILT_TIMER, + EV_ADD|EV_ONESHOT, NOTE_SECONDS, j->exit_timeout, j) != -1); + } + + job_log(j, LOG_DEBUG, "Sent SIGTERM signal"); } - - job_log(j, LOG_DEBUG, "Sent SIGTERM signal"); } INTERNAL_ABI launch_data_t @@ -1453,6 +1476,9 @@ if (strcasecmp(key, LAUNCH_JOBKEY_ENABLEGLOBBING) == 0) { j->globargv = value; found_key = true; + } else if (strcasecmp(key, LAUNCH_JOBKEY_ENABLETRANSACTIONS) == 0) { + j->kill_via_shmem = value; + found_key = true; } else if (strcasecmp(key, LAUNCH_JOBKEY_ENTERKERNELDEBUGGERBEFOREKILL) == 0) { j->debug_before_kill = value; found_key = true; @@ -2203,6 +2229,10 @@ job_log(j, LOG_DEBUG, "Reaping"); + if (j->shmem) { + job_assumes(j, munmap(j->shmem, getpagesize()) == 0); + } + if (unlikely(j->weird_bootstrap)) { mach_msg_size_t mxmsgsz = sizeof(union __RequestUnion__job_mig_protocol_vproc_subsystem); @@ -2335,6 +2365,7 @@ } j->last_exit_status = status; j->sent_sigkill = false; + j->sent_kill_via_shmem = false; j->p = 0; /* @@ -5402,6 +5433,36 @@ } kern_return_t +job_mig_setup_shmem(job_t j, mach_port_t *shmem_port) +{ + memory_object_size_t size_of_page, size_of_page_orig; + kern_return_t kr; + + if (!launchd_assumes(j != NULL)) { + return BOOTSTRAP_NO_MEMORY; + } + + if (unlikely(j->anonymous)) { + return BOOTSTRAP_NOT_PRIVILEGED; + } + + size_of_page_orig = size_of_page = getpagesize(); + + if (!job_assumes(j, j->shmem = mmap(NULL, size_of_page, PROT_READ|PROT_WRITE, MAP_ANON, -1, 0))) { + return BOOTSTRAP_NO_MEMORY; + } + + kr = mach_make_memory_entry_64(mach_task_self(), &size_of_page, + (memory_object_offset_t)((long)j->shmem), VM_PROT_DEFAULT, shmem_port, 0); + + if (job_assumes(j, kr == 0)) { + job_assumes(j, size_of_page == size_of_page_orig); + } + + return kr; +} + +kern_return_t job_mig_create_server(job_t j, cmd_t server_cmd, uid_t server_uid, boolean_t on_demand, mach_port_t *server_portp) { struct ldcred *ldc = runtime_get_caller_creds(); Modified: trunk/launchd/src/liblaunch_public.h =================================================================== --- trunk/launchd/src/liblaunch_public.h 2008-03-27 01:40:55 UTC (rev 23573) +++ trunk/launchd/src/liblaunch_public.h 2008-03-27 21:18:33 UTC (rev 23574) @@ -101,6 +101,7 @@ #define LAUNCH_JOBKEY_LAUNCHONLYONCE "LaunchOnlyOnce" #define LAUNCH_JOBKEY_ABANDONPROCESSGROUP "AbandonProcessGroup" #define LAUNCH_JOBKEY_POLICIES "Policies" +#define LAUNCH_JOBKEY_ENABLETRANSACTIONS "EnableTransactions" #define LAUNCH_JOBPOLICY_DENYCREATINGOTHERJOBS "DenyCreatingOtherJobs" Modified: trunk/launchd/src/libvproc.c =================================================================== --- trunk/launchd/src/libvproc.c 2008-03-27 01:40:55 UTC (rev 23573) +++ trunk/launchd/src/libvproc.c 2008-03-27 21:18:33 UTC (rev 23574) @@ -32,6 +32,7 @@ #include <unistd.h> #include <syslog.h> #include <pthread.h> +#include <signal.h> #if HAVE_QUARANTINE #include <quarantine.h> #endif @@ -44,10 +45,142 @@ #include "reboot2.h" +#define likely(x) __builtin_expect((bool)(x), true) +#define unlikely(x) __builtin_expect((bool)(x), false) + static mach_port_t get_root_bootstrap_port(void); +const char *__crashreporter_info__; /* this should get merged with other versions of the symbol */ + static int64_t cached_pid = -1; +static struct vproc_shmem_s *vproc_shmem; +static void +vproc_shmem_init(void) +{ + vm_address_t vm_addr = 0; + mach_port_t shmem_port; + kern_return_t kr; + + kr = vproc_mig_setup_shmem(bootstrap_port, &shmem_port); + + if (unlikely(kr)) { + abort(); + } + + kr = vm_map_64(mach_task_self(), &vm_addr, getpagesize(), 0, true, shmem_port, 0, false, + VM_PROT_READ|VM_PROT_WRITE, VM_PROT_READ|VM_PROT_WRITE, VM_INHERIT_NONE); + + if (unlikely(kr)) { + abort(); + } + + vproc_shmem = (struct vproc_shmem_s *)vm_addr; +} + +vproc_transaction_t +vproc_transaction_begin(vproc_t vp __attribute__((unused))) +{ + vproc_transaction_t vpt = (vproc_transaction_t)vproc_shmem_init; /* we need a "random" variable that is testable */ + + _basic_vproc_transaction_begin(); + + return vpt; +} + +void +_basic_vproc_transaction_begin(void) +{ + int64_t newval; + + if (unlikely(vproc_shmem == NULL)) { + vproc_shmem_init(); + if (vproc_shmem == NULL) { + return; + } + } + + newval = __sync_add_and_fetch(&vproc_shmem->vp_shmem_transaction_cnt, 1); + + if (unlikely(newval < 1)) { + if (vproc_shmem->vp_shmem_flags & VPROC_SHMEM_EXITING) { + raise(SIGKILL); + __crashreporter_info__ = "raise(SIGKILL) failed"; + } else { + __crashreporter_info__ = "Unbalanced: vproc_transaction_begin()"; + } + abort(); + } +} + +void +vproc_transaction_end(vproc_t vp __attribute__((unused)), vproc_transaction_t vpt) +{ + if (unlikely(vpt != (vproc_transaction_t)vproc_shmem_init)) { + __crashreporter_info__ = "Bogus transaction handle passed to vproc_transaction_end() "; + abort(); + } + + _basic_vproc_transaction_end(); +} + +void +_basic_vproc_transaction_end(void) +{ + int32_t newval = __sync_sub_and_fetch(&vproc_shmem->vp_shmem_transaction_cnt, 1); + + if (unlikely(newval < 0)) { + if (vproc_shmem->vp_shmem_flags & VPROC_SHMEM_EXITING) { + raise(SIGKILL); + __crashreporter_info__ = "raise(SIGKILL) failed"; + } else { + __crashreporter_info__ = "Unbalanced: vproc_transaction_end()"; + } + abort(); + } +} + +vproc_standby_t +vproc_standby_begin(vproc_t vp __attribute__((unused))) +{ + vproc_standby_t vpsb = (vproc_standby_t)vproc_shmem_init; /* we need a "random" variable that is testable */ + int64_t newval; + + if (unlikely(vproc_shmem == NULL)) { + vproc_shmem_init(); + if (vproc_shmem == NULL) { + return NULL; + } + } + + newval = __sync_add_and_fetch(&vproc_shmem->vp_shmem_standby_cnt, 1); + + if (unlikely(newval < 1)) { + __crashreporter_info__ = "Unbalanced: vproc_standby_begin()"; + abort(); + } + + return vpsb; +} + +void +vproc_standby_end(vproc_t vp __attribute__((unused)), vproc_standby_t vpt) +{ + int32_t newval; + + if (unlikely(vpt != (vproc_standby_t)vproc_shmem_init)) { + __crashreporter_info__ = "Bogus standby handle passed to vproc_standby_end() "; + abort(); + } + + newval = __sync_sub_and_fetch(&vproc_shmem->vp_shmem_standby_cnt, 1); + + if (unlikely(newval < 0)) { + __crashreporter_info__ = "Unbalanced: vproc_standby_end()"; + abort(); + } +} + kern_return_t _vproc_grab_subset(mach_port_t bp, mach_port_t *reqport, mach_port_t *rcvright, launch_data_t *outval, mach_port_array_t *ports, mach_msg_type_number_t *portCnt) Modified: trunk/launchd/src/libvproc_internal.h =================================================================== --- trunk/launchd/src/libvproc_internal.h 2008-03-27 01:40:55 UTC (rev 23573) +++ trunk/launchd/src/libvproc_internal.h 2008-03-27 21:18:33 UTC (rev 23574) @@ -33,6 +33,14 @@ typedef pid_t * pid_array_t; typedef mach_port_t vproc_mig_t; +#define VPROC_SHMEM_EXITING 0x1 + +struct vproc_shmem_s { + int32_t vp_shmem_transaction_cnt; + int32_t vp_shmem_standby_cnt; + int32_t vp_shmem_flags; +}; + #ifdef protocol_vproc_MSG_COUNT /* HACK */ #include "launchd_core_logic.h" Modified: trunk/launchd/src/libvproc_private.h =================================================================== --- trunk/launchd/src/libvproc_private.h 2008-03-27 01:40:55 UTC (rev 23573) +++ trunk/launchd/src/libvproc_private.h 2008-03-27 21:18:33 UTC (rev 23574) @@ -76,6 +76,9 @@ vproc_err_t _vprocmgr_move_subset_to_user(uid_t target_user, const char *session_type); +void _basic_vproc_transaction_begin(void); +void _basic_vproc_transaction_end(void); + #pragma GCC visibility pop __END_DECLS Modified: trunk/launchd/src/libvproc_public.h =================================================================== --- trunk/launchd/src/libvproc_public.h 2008-03-27 01:40:55 UTC (rev 23573) +++ trunk/launchd/src/libvproc_public.h 2008-03-27 21:18:33 UTC (rev 23574) @@ -67,8 +67,8 @@ * * launchd jobs should update their property lists accordingly. * - * LaunchServices uses private methods to coordinate whether GUI applications - * have opted into this design. + * We plan to have LaunchServices use private methods to coordinate + * whether GUI applications have opted into this design. */ /*! @@ -80,22 +80,28 @@ typedef struct vproc_transaction_s *vproc_transaction_t; /*! - * @function vproc_transaction_prepare + * @function vproc_transaction_begin * + * @param virtual_proc + * This is meant for future API improvements. Pass NULL for now. + * * @result - * Returns an opaque handle to be passed to vproc_transaction_complete(). + * Returns an opaque handle to be passed to vproc_transaction_end(). * * @abstract * Call this API before creating data that needs to be saved via I/O later. */ vproc_transaction_t -vproc_transaction_prepare(void); +vproc_transaction_begin(vproc_t virtual_proc); /*! - * @function vproc_transaction_complete + * @function vproc_transaction_end * + * @param virtual_proc + * This is meant for future API improvements. Pass NULL for now. + * * @param handle - * The handle previously created with vproc_transaction_prepare(). + * The handle previously created with vproc_transaction_begin(). * * @abstract * Call this API after the data has either been flushed or otherwise resolved. @@ -104,7 +110,7 @@ * Calling this API with the same handle more than once is undefined. */ void -vproc_transaction_complete(vproc_transaction_t handle); +vproc_transaction_end(vproc_t virtual_proc, vproc_transaction_t handle); /*! * @typedef vproc_standby_t @@ -115,23 +121,29 @@ typedef struct vproc_standby_s *vproc_standby_t; /*! - * @function vproc_standby_prepare + * @function vproc_standby_begin * + * @param virtual_proc + * This is meant for future API improvements. Pass NULL for now. + * * @result - * Returns an opaque handle to be passed to vproc_standby_complete(). + * Returns an opaque handle to be passed to vproc_standby_end(). * * @abstract * Call this API before registering notifications. For example: timers network * state change, or when monitoring keyboard/mouse events. */ vproc_standby_t -vproc_standby_prepare(void); +vproc_standby_begin(vproc_t virtual_proc); /*! - * @function vproc_standby_complete + * @function vproc_standby_end * + * @param virtual_proc + * This is meant for future API improvements. Pass NULL for now. + * * @param handle - * The handle previously created with vproc_standby_prepare(). + * The handle previously created with vproc_standby_begin(). * * @abstract * Call this API when deregistering notifications. @@ -140,7 +152,7 @@ * Calling this API with the same handle more than once is undefined. */ void -vproc_standby_complete(vproc_standby_t handle); +vproc_standby_end(vproc_t virtual_proc, vproc_standby_t handle); #pragma GCC visibility pop Modified: trunk/launchd/src/protocol_job.defs =================================================================== --- trunk/launchd/src/protocol_job.defs 2008-03-27 01:40:55 UTC (rev 23573) +++ trunk/launchd/src/protocol_job.defs 2008-03-27 21:18:33 UTC (rev 23574) @@ -95,7 +95,9 @@ __requestor_port: mach_port_t; out __subset_port : mach_port_make_send_t); -skip; /* create_service prior to 10.6 */ +routine setup_shmem( + __bs_port : job_t; + out __shmem_port : mach_port_move_send_t); routine take_subset( __bs_port : job_t;