summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Huddleston <jeremyhu@apple.com>2011-04-25 23:09:03 -0700
committerJeremy Huddleston <jeremyhu@apple.com>2011-04-26 12:54:46 -0700
commit5f5390886004f785b4338fcff27f287c9aa6cd8c (patch)
tree48147a155ec27823c63027b0a551e7b9304459a9
parentc6a7f895c8c90b85ed4fd08927c47b0d17ed067a (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.am2
-rw-r--r--launchd/console_redirect.c229
-rw-r--r--launchd/privileged_startx/Makefile.am1
-rw-r--r--launchd/privileged_startx/server.c18
-rw-r--r--launchd/user_startx/.gitignore1
-rw-r--r--launchd/user_startx/Makefile.am12
-rw-r--r--launchd/user_startx/launchd_startx.c59
-rw-r--r--launchd/user_startx/startx.plist.cpp1
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>