diff options
author | Jeremy Huddleston <jeremyhu@apple.com> | 2011-04-25 23:09:03 -0700 |
---|---|---|
committer | Jeremy Huddleston <jeremyhu@apple.com> | 2011-04-26 12:54:46 -0700 |
commit | 5f5390886004f785b4338fcff27f287c9aa6cd8c (patch) | |
tree | 48147a155ec27823c63027b0a551e7b9304459a9 | |
parent | c6a7f895c8c90b85ed4fd08927c47b0d17ed067a (diff) |
launchd: Enable stdout/stderr redirection to ASL
Don't rely on launchd to do our logging for us.
Signed-off-by: Jeremy Huddleston <jeremyhu@apple.com>
Reviewed-by: Daniel A. Steffen <dsteffen@apple.com>
-rw-r--r-- | launchd/Makefile.am | 2 | ||||
-rw-r--r-- | launchd/console_redirect.c | 229 | ||||
-rw-r--r-- | launchd/privileged_startx/Makefile.am | 1 | ||||
-rw-r--r-- | launchd/privileged_startx/server.c | 18 | ||||
-rw-r--r-- | launchd/user_startx/.gitignore | 1 | ||||
-rw-r--r-- | launchd/user_startx/Makefile.am | 12 | ||||
-rw-r--r-- | launchd/user_startx/launchd_startx.c | 59 | ||||
-rw-r--r-- | launchd/user_startx/startx.plist.cpp | 1 |
8 files changed, 318 insertions, 5 deletions
diff --git a/launchd/Makefile.am b/launchd/Makefile.am index d6b5597..8add783 100644 --- a/launchd/Makefile.am +++ b/launchd/Makefile.am @@ -1 +1,3 @@ SUBDIRS = privileged_startx user_startx + +EXTRA_DIST = asl_redirect.c diff --git a/launchd/console_redirect.c b/launchd/console_redirect.c new file mode 100644 index 0000000..dda84f1 --- /dev/null +++ b/launchd/console_redirect.c @@ -0,0 +1,229 @@ +/* Copyright (c) 2011 Apple Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT + * HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name(s) of the above + * copyright holders shall not be used in advertising or otherwise to + * promote the sale, use or other dealings in this Software without + * prior written authorization. + */ + +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/event.h> +#include <asl.h> +#include <errno.h> +#include <fcntl.h> +#include <pthread.h> + +#define BUF_SIZE 1024 + +typedef struct { + /* Initialized values */ + int fd; + int level; + aslclient aslc; + aslmsg aslm; + + /* Buffer for reading */ + char buf[BUF_SIZE]; + char *w; + int closed; +} asl_redirect; + +/* Redirect stdout/stderr to asl */ +static void *redirect_thread(void *ctx) { + asl_redirect *fds = ctx; + char *p, *s; + ssize_t nbytes; + struct kevent ev[2]; + int kq, n; + + /* Setup our kqueue */ + kq = kqueue(); + EV_SET(&ev[0], fds[0].fd, EVFILT_READ, EV_ADD, 0, 0, 0); + EV_SET(&ev[1], fds[1].fd, EVFILT_READ, EV_ADD, 0, 0, 0); + n = kevent(kq, ev, 2, NULL, 0, NULL); + + /* Set our buffers to empty */ + fds[0].w = fds[0].buf; + fds[1].w = fds[1].buf; + + /* Start off open */ + fds[0].closed = fds[1].closed = 0; + + while(!(fds[0].closed && fds[1].closed)) { + n = kevent(kq, NULL, 0, ev, 1, NULL); + if(n < 0) { + asl_log(fds[1].aslc, fds[1].aslm, ASL_LEVEL_ERR, "read failure: %s", strerror(errno)); + break; + } + + if(n == 1 && ev->filter == EVFILT_READ) { + int fd = ev->ident; + asl_redirect *aslr; + + if(fd == fds[0].fd) { + aslr = &fds[0]; + } else if(fd == fds[1].fd) { + aslr = &fds[1]; + } else { + asl_log(fds[1].aslc, fds[1].aslm, ASL_LEVEL_ERR, "unexpected file descriptor: %d", fd); + break; + } + + if(ev->flags & EV_EOF) { + EV_SET(&ev[1], aslr->fd, EVFILT_READ, EV_DELETE, 0, 0, 0); + kevent(kq, &ev[1], 1, NULL, 0, NULL); + close(aslr->fd); + aslr->closed = 1; + continue; + } + + nbytes = read(fd, aslr->w, BUF_SIZE - (aslr->w - aslr->buf) - 1); + if(nbytes > 0) { + nbytes += (aslr->w - aslr->buf); + aslr->buf[nbytes] = '\0'; + + /* One line at a time */ + for(p=aslr->buf; *p && (p - aslr->buf) < nbytes; p = s + 1) { + // Find null or \n + for(s=p; *s && *s != '\n'; s++); + if(*s == '\n') { + *s='\0'; + asl_log(aslr->aslc, aslr->aslm, aslr->level, "%s", p); + } else if(aslr->buf != p) { + memmove(aslr->buf, p, BUF_SIZE); + aslr->w = aslr->buf + (s - p); + break; + } else if(nbytes == BUF_SIZE - 1) { + asl_log(aslr->aslc, aslr->aslm, aslr->level, "%s", p); + aslr->w = aslr->buf; + break; + } + } + } + } + } + + return NULL; +} + +static pthread_t redirect_pthread; +static void redirect_atexit(void) { + /* stdout is linebuffered, so flush the buffer */ + fflush(stdout); + + /* close our pipes, causing the redirect thread to terminate */ + close(STDOUT_FILENO); + close(STDERR_FILENO); + pthread_join(redirect_pthread, NULL); +} + +int console_redirect(aslclient aslc, aslmsg aslm, int stdout_level, int stderr_level) { + int err; + int outpair[2]; + int errpair[2]; + static asl_redirect readpair[2]; + + /* Create pipes */ + if(pipe(outpair) == -1) { + asl_log(aslc, aslm, ASL_LEVEL_ERR, "pipe() failed: %s", strerror(errno)); + return errno; + } + + if(pipe(errpair) == -1) { + asl_log(aslc, aslm, ASL_LEVEL_ERR, "pipe() failed: %s", strerror(errno)); + close(outpair[0]); + close(outpair[1]); + return errno; + } + + /* Close the read fd but not the write fd on exec */ + fcntl(outpair[0], F_SETFD, FD_CLOEXEC); + fcntl(errpair[0], F_SETFD, FD_CLOEXEC); + + /* Setup the context to handoff to the read thread */ + readpair[0].fd = outpair[0]; + readpair[0].level = stdout_level; + readpair[0].aslc = aslc; + readpair[0].aslm = aslm; + + readpair[1].fd = errpair[0]; + readpair[1].level = stderr_level; + readpair[1].aslc = aslc; + readpair[1].aslm = aslm; + + /* Handoff to the read thread */ + if((err = pthread_create(&redirect_pthread, NULL, redirect_thread, readpair)) != 0) { + asl_log(aslc, aslm, ASL_LEVEL_ERR, "pthread_create() failed: %s", strerror(err)); + close(outpair[0]); + close(outpair[1]); + close(errpair[0]); + close(errpair[1]); + return err; + } + + /* Replace our stdout fd. force stdout to be line buffered since it defaults to buffered when not a tty */ + if(dup2(outpair[1], STDOUT_FILENO) == -1) { + asl_log(aslc, aslm, ASL_LEVEL_ERR, "dup2(stdout) failed: %s", strerror(errno)); + } else if(setlinebuf(stdout) != 0) { + asl_log(aslc, aslm, ASL_LEVEL_ERR, "setlinebuf(stdout) failed, log redirection may be delayed."); + } + + /* Replace our stderr fd. stderr is always unbuffered */ + if(dup2(errpair[1], STDERR_FILENO) == -1) { + asl_log(aslc, aslm, ASL_LEVEL_ERR, "dup2(stderr) failed: %s", strerror(errno)); + } + + /* Close the duplicate fds since they've been reassigned */ + close(outpair[1]); + close(errpair[1]); + + /* Register an atexit handler to wait for the logs to flush */ + if(atexit(redirect_atexit) != 0) { + asl_log(aslc, aslm, ASL_LEVEL_ERR, "atexit(redirect_atexit) failed: %s", strerror(errno)); + } + + return 0; +} + +#ifdef DEBUG_CONSOLE_REDIRECT +int main(int argc, char **argv) { + console_redirect(NULL, NULL, ASL_LEVEL_NOTICE, ASL_LEVEL_ERR); + + fprintf(stderr, "TEST ERR1\n"); + fprintf(stdout, "TEST OUT1\n"); + fprintf(stderr, "TEST ERR2\n"); + fprintf(stdout, "TEST OUT2\n"); + system("/bin/echo SYST OUT"); + system("/bin/echo SYST ERR >&2"); + fprintf(stdout, "TEST OUT3\n"); + fprintf(stdout, "TEST OUT4\n"); + fprintf(stderr, "TEST ERR3\n"); + fprintf(stderr, "TEST ERR4\n"); + + exit(0); +} +#endif diff --git a/launchd/privileged_startx/Makefile.am b/launchd/privileged_startx/Makefile.am index 2cf4c82..d0f2322 100644 --- a/launchd/privileged_startx/Makefile.am +++ b/launchd/privileged_startx/Makefile.am @@ -41,6 +41,7 @@ CPP_FILES_FLAGS += -DTIGER_LAUNCHD endif dist_privileged_startx_SOURCES = \ + ../console_redirect.c \ server.c \ client.c \ privileged_startx.c diff --git a/launchd/privileged_startx/server.c b/launchd/privileged_startx/server.c index dfe2c58..d513ec0 100644 --- a/launchd/privileged_startx/server.c +++ b/launchd/privileged_startx/server.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2008 Apple Inc. +/* Copyright (c) 2008-2011 Apple Inc. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation files @@ -77,6 +77,9 @@ struct idle_globals idle_globals; /* Default script dir */ const char *script_dir = SCRIPTDIR; +/* console_redirect.c */ +extern int console_redirect(aslclient aslc, aslmsg amsg, int stdout_level, int stderr_level); + #ifndef LAUNCH_JOBKEY_MACHSERVICES static mach_port_t checkin_or_register(char *bname) { kern_return_t kr; @@ -118,7 +121,10 @@ int server_main(const char *dir) { long idle_timeout = DEFAULT_IDLE_TIMEOUT; #endif - launch_data_t config = NULL, checkin = NULL; + launch_data_t config = NULL, checkin = NULL, label = NULL; + const char *labelstr = "privileged_startx"; + aslclient aslc; + checkin = launch_data_new_string(LAUNCH_KEY_CHECKIN); config = launch_msg(checkin); if (!config || launch_data_get_type(config) == LAUNCH_DATA_ERRNO) { @@ -132,6 +138,14 @@ int server_main(const char *dir) { "script directory set: %s", script_dir); } + label = launch_data_dict_lookup(config, LAUNCH_JOBKEY_LABEL); + if (label) { + labelstr = launch_data_get_string(label); + } + + aslc = asl_open(labelstr, "user", ASL_OPT_NO_DELAY); + (void)console_redirect(aslc, NULL, ASL_LEVEL_INFO, ASL_LEVEL_NOTICE); + #ifdef LAUNCH_JOBKEY_MACHSERVICES launch_data_t tmv; tmv = launch_data_dict_lookup(config, LAUNCH_JOBKEY_TIMEOUT); diff --git a/launchd/user_startx/.gitignore b/launchd/user_startx/.gitignore index 60802d7..b87dd68 100644 --- a/launchd/user_startx/.gitignore +++ b/launchd/user_startx/.gitignore @@ -1 +1,2 @@ +launchd_startx *.startx.plist* diff --git a/launchd/user_startx/Makefile.am b/launchd/user_startx/Makefile.am index 600d325..30270dd 100644 --- a/launchd/user_startx/Makefile.am +++ b/launchd/user_startx/Makefile.am @@ -23,10 +23,16 @@ include $(top_srcdir)/cpprules.in xinitrcdir = $(XINITDIR) +xinitrc_PROGRAMS = launchd_startx + +AM_CPPFLAGS = -DXINITDIR=\"$(xinitrcdir)\" -DBINDIR=\"$(bindir)\" -DLAUNCHD_ID_PREFIX=\"$(launchdidprefix)\" + +dist_launchd_startx_SOURCES = \ + ../console_redirect.c \ + launchd_startx.c + CPP_FILES_FLAGS = \ - -DXINITDIR=$(XINITDIR) $(PROGCPPDEFS) -DLIBDIR=$(libdir) \ - -DSHELL_CMD=$(SHELL_CMD) $(STARTX_COOKIE_FLAGS) \ - -D__libexecdir__="$(libexecdir)" \ + -D__xinitrcdir__="$(xinitrcdir)" \ -D__bindir__="$(bindir)" \ -DLAUNCHD_ID_PREFIX="$(launchdidprefix)" diff --git a/launchd/user_startx/launchd_startx.c b/launchd/user_startx/launchd_startx.c new file mode 100644 index 0000000..cf93227 --- /dev/null +++ b/launchd/user_startx/launchd_startx.c @@ -0,0 +1,59 @@ +/* Copyright (c) 2011 Apple Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT + * HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name(s) of the above + * copyright holders shall not be used in advertising or otherwise to + * promote the sale, use or other dealings in this Software without + * prior written authorization. + */ + +#include <asl.h> +#include <unistd.h> +#include <stdio.h> +#include <assert.h> +#include <spawn.h> +#include <sys/wait.h> +#include <string.h> +#include <stdlib.h> + +/* console_redirect.c */ +extern int console_redirect(aslclient aslc, aslmsg amsg, int stdout_level, int stderr_level); + +int main(int argc, char **argv, char **envp) { + aslclient aslc; + pid_t child; + int pstat; + + if(argc < 2 || strcmp(argv[1], "--help")) { + fprintf(stderr, "Usage: %s prog [args...]\n", argv[0]); + exit(EXIT_FAILURE); + } + + aslc = asl_open(LAUNCHD_ID_PREFIX".startx", "user", ASL_OPT_NO_DELAY); + (void)console_redirect(aslc, NULL, ASL_LEVEL_INFO, ASL_LEVEL_NOTICE); + + assert(posix_spawnp(&child, argv[1], NULL, NULL, &argv[1], envp) == 0); + + wait4(child, &pstat, 0, (struct rusage *)0); + + return pstat; +} diff --git a/launchd/user_startx/startx.plist.cpp b/launchd/user_startx/startx.plist.cpp index bd4c9e9..1a87527 100644 --- a/launchd/user_startx/startx.plist.cpp +++ b/launchd/user_startx/startx.plist.cpp @@ -6,6 +6,7 @@ <string>LAUNCHD_ID_PREFIX.startx</string> <key>ProgramArguments</key> <array> + <string>__xinitrcdir__/launchd_startx</string> <string>__bindir__/startx</string> </array> <key>Sockets</key> |