diff options
-rw-r--r-- | agent/tcf/framework/mdep.c | 329 | ||||
-rw-r--r-- | agent/tcf/framework/mdep.h | 7 | ||||
-rw-r--r-- | agent/tcf/framework/trace.c | 13 | ||||
-rw-r--r-- | agent/tcf/main/main.c | 28 |
4 files changed, 362 insertions, 15 deletions
diff --git a/agent/tcf/framework/mdep.c b/agent/tcf/framework/mdep.c index a6c2a8d3..56a4d9fc 100644 --- a/agent/tcf/framework/mdep.c +++ b/agent/tcf/framework/mdep.c @@ -1199,7 +1199,202 @@ const char * loc_gai_strerror(int ecode) { #endif -#if defined(_WIN32) || defined(_WRS_KERNEL) || defined (__SYMBIAN32__) +#if defined(_WIN32) +# include <tlhelp32.h> +# ifdef _MSC_VER +# pragma warning(disable:4201) /* nonstandard extension used : nameless struct/union (in winternl.h) */ +# include <winternl.h> +# else +# include <ntdef.h> +# endif +# ifndef STATUS_INFO_LENGTH_MISMATCH +# define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L) +# endif +# ifndef SystemHandleInformation +# define SystemHandleInformation 16 +# endif + +/* Disable inheritance for all handles owned by process */ +static NTSTATUS disable_handle_inheritance(void) { + typedef struct _HANDLE_INFORMATION { + USHORT ProcessId; + USHORT CreatorBackTraceIndex; + UCHAR ObjectTypeNumber; + UCHAR Flags; + USHORT Handle; + PVOID Object; + ACCESS_MASK GrantedAccess; + } HANDLE_INFORMATION; + typedef struct _SYSTEM_HANDLE_INFORMATION { + ULONG Count; + HANDLE_INFORMATION Handles[1]; + } SYSTEM_HANDLE_INFORMATION; + typedef NTSTATUS (FAR WINAPI * QuerySystemInformationTypedef)(int, PVOID, ULONG, PULONG); + QuerySystemInformationTypedef QuerySystemInformationProc = (QuerySystemInformationTypedef)GetProcAddress( + GetModuleHandle("NTDLL.DLL"), "NtQuerySystemInformation"); + DWORD size; + NTSTATUS status; + SYSTEM_HANDLE_INFORMATION * hi = NULL; + + size = sizeof(SYSTEM_HANDLE_INFORMATION) + sizeof(HANDLE_INFORMATION) * 256; + hi = (SYSTEM_HANDLE_INFORMATION *)tmp_alloc_zero(size); + for (;;) { + status = QuerySystemInformationProc(SystemHandleInformation, hi, size, &size); + if (status != STATUS_INFO_LENGTH_MISMATCH) break; + hi = (SYSTEM_HANDLE_INFORMATION *)tmp_realloc(hi, size); + } + if (status == 0) { + ULONG i; + DWORD id = GetCurrentProcessId(); + for (i = 0; i < hi->Count; i++) { + if (hi->Handles[i].ProcessId != id) continue; + SetHandleInformation((HANDLE)(uintptr_t)hi->Handles[i].Handle, HANDLE_FLAG_INHERIT, 0); + } + } + return status; +} + +static char *make_cmd_from_args(char **args) { + int i = 0; + int cmd_size = 0; + int cmd_pos = 0; + char *cmd = NULL; + +# define cmd_append(ch) { \ + if (cmd_pos >= cmd_size) { \ + cmd_size += 0x1000; \ + cmd = (char *)loc_realloc(cmd, cmd_size); \ + } \ + cmd[cmd_pos++] = (ch); \ + } + while (args[i] != NULL) { + const char * p = args[i++]; + if (cmd_pos > 0) cmd_append(' '); + cmd_append('"'); + while (*p) { + if (*p == '"') cmd_append('\\'); + cmd_append(*p); + p++; + } + cmd_append('"'); + } + cmd_append(0); +# undef cmd_append + return cmd; +} + +static int running_as_daemon = 0; + +int is_daemon(void) { + return running_as_daemon; +} + +# if !defined(__CYGWIN__) +# define pipe(fds) _pipe((fds), 1024, 0) +# endif + +void become_daemon(char **args) { + int fdpairs[4]; + int npairs = 2; + char fnm[FILE_PATH_SIZE]; + STARTUPINFO startupInfo; + PROCESS_INFORMATION prs_info; + int i; + + assert(!running_as_daemon); + running_as_daemon = 1; + + if (args == NULL) + return; + + fflush(stdout); + fflush(stderr); + + /* Make sure no handles are inherited by new process */ + disable_handle_inheritance(); + + if (pipe(fdpairs) < 0) { + perror("pipe"); + exit(1); + } + + if (pipe(fdpairs + 2) < 0) { + perror("pipe"); + exit(1); + } + + if (GetModuleFileName(NULL, fnm, sizeof(fnm)) == 0) { + fprintf(stderr, "GetModuleFileName failed\n"); + exit(1); + } + + memset(&startupInfo, 0, sizeof startupInfo); + startupInfo.dwFlags |= STARTF_USESTDHANDLES; + startupInfo.hStdInput = INVALID_HANDLE_VALUE; + startupInfo.hStdOutput = (HANDLE)_get_osfhandle(fdpairs[1]); + startupInfo.hStdError = (HANDLE)_get_osfhandle(fdpairs[3]); + SetHandleInformation(startupInfo.hStdOutput, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT); + SetHandleInformation(startupInfo.hStdError, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT); + + if (!CreateProcess(fnm, make_cmd_from_args(args), NULL, NULL, TRUE, + DETACHED_PROCESS, NULL, NULL, &startupInfo, &prs_info)) { + fprintf(stderr, "CreateProcess failed 0x%lx\n", GetLastError()); + exit(1); + } + + for (i = 0; i < 2; i++) { + /* Make read side non-blocking */ + DWORD pipemode = PIPE_READMODE_BYTE | PIPE_NOWAIT; + if (!SetNamedPipeHandleState((HANDLE)_get_osfhandle(fdpairs[i*2]), &pipemode, NULL, NULL)) { + fprintf(stderr, "SetNamedPipeHandleState failed 0x%lx\n", GetLastError()); + exit(1); + } + + /* Close write side of pipes so we get end of file as soon as + * the new them or exits */ + close(fdpairs[i*2 + 1]); + fdpairs[i*2 + 1] = 1; + } + + while (npairs > 0) { + for (i = 0; i < npairs; i++) { + char tmpbuf[1000]; + DWORD size; + if (ReadFile((HANDLE)_get_osfhandle(fdpairs[i*2]), tmpbuf, sizeof tmpbuf, &size, NULL)) { + if (write(fdpairs[i*2 + 1], tmpbuf, size) < 0) + perror("write"); + } else if (GetLastError() != ERROR_NO_DATA) { + if (GetLastError() != ERROR_BROKEN_PIPE) { + fprintf(stderr, "SetNamedPipeHandleState failed 0x%lx\n", GetLastError()); + } + fdpairs[i*2] = fdpairs[(npairs - 1)*2]; + fdpairs[i*2 + 1] = fdpairs[(npairs - 1)*2 + 1]; + npairs--; + } + } + /* Neither select nor overlapped I/O works for anonymous pipes, so use + * polling for now until a better solution if found... */ + usleep(1000); + } + + exit(0); +} + +void close_out_and_err(void) { + int fd = open("nul", O_WRONLY, 0); + if (fd < 0) { + perror("open nul"); + exit(1); + } + + fflush(stdout); + fflush(stderr); + + dup2(fd, 1); + dup2(fd, 2); +} + +#elif defined(_WRS_KERNEL) || defined (__SYMBIAN32__) int is_daemon(void) { return 0; @@ -1210,9 +1405,10 @@ void become_daemon(void) { exit(1); } -#else +void close_out_and_err(void) { +} -#include <syslog.h> +#else static int running_as_daemon = 0; @@ -1221,13 +1417,132 @@ int is_daemon(void) { } void become_daemon(void) { + int fdpairs[4]; + int npairs = 2; + assert(!running_as_daemon); - openlog("tcf-agent", LOG_PID, LOG_DAEMON); - if (daemon(0, 0) < 0) { - syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "Cannot become a daemon: %m"); + + fflush(stdout); + fflush(stderr); + + if (pipe(fdpairs) < 0) { + perror("pipe"); exit(1); } - running_as_daemon = 1; + + if (pipe(fdpairs + 2) < 0) { + perror("pipe"); + exit(1); + } + + /* Fork a new process so we can close everything except the pipes */ + switch (fork()) { + default: /* Parent process */ + /* Close write side of pipes so we get end of file as soon as + * child closes them or exits */ + close(fdpairs[1]); + close(fdpairs[3]); + fdpairs[1] = 1; + fdpairs[3] = 2; + + while (npairs > 0) { + int i; + int rval; + int nfds = 0; + fd_set readfds; + char tmpbuf[1000]; + + FD_ZERO(&readfds); + for (i = 0; i < npairs; i++) { + int fd = fdpairs[i*2]; + FD_SET(fd, &readfds); + if (nfds < fd) + nfds = fd; + } + rval = select(nfds + 1, &readfds, NULL, NULL, NULL); + if (rval < 0) { + if (errno == EINTR) + continue; + perror("select"); + _exit(1); + } + assert(rval > 0); + for (i = 0; i < npairs; i++) { + int fd = fdpairs[i*2]; + if (FD_ISSET(fd, &readfds)) { + rval = read(fd, tmpbuf, sizeof tmpbuf); + if (rval > 0) { + if (write(fdpairs[i*2 + 1], tmpbuf, rval) < 0) + perror("write"); + } else { + if (rval < 0) + perror("read"); + fdpairs[i*2] = fdpairs[(npairs - 1)*2]; + fdpairs[i*2 + 1] = fdpairs[(npairs - 1)*2 + 1]; + npairs--; + } + } + } + } + _exit(0); + break; + + case 0: { /* Child process */ + /* Replace stdin with /dev/null */ + int fd = open("/dev/null", O_RDONLY); + if (fd < 0) { + perror("open /dev/null"); + exit(1); + } + dup2(fd, 0); + dup2(fdpairs[1], 1); + dup2(fdpairs[3], 2); + + /* Close all open files except stdin, stdout and stderr */ + fd = sysconf(_SC_OPEN_MAX); + while (fd-- > 3) + close(fd); + + /* Fork a new process so it is owned by init */ + switch (fork()) { + default: /* Parent process */ + _exit(0); + break; + + case 0: /* Child process */ + /* Create new session */ + if (setsid() == (pid_t)-1) { + perror("setsid"); + _exit(1); + } + + running_as_daemon = 1; + return; + + case -1: /* Fork failed */ + perror("fork"); + exit(1); + } + } + + case -1: /* Fork failed */ + perror("fork"); + exit(1); + } +} + +void close_out_and_err(void) { + int fd = open("/dev/null", O_WRONLY); + if (fd < 0) { + perror("open /dev/null"); + exit(1); + } + + fflush(stdout); + fflush(stderr); + + dup2(fd, 1); + dup2(fd, 2); } #endif diff --git a/agent/tcf/framework/mdep.h b/agent/tcf/framework/mdep.h index 3c25673f..e449a595 100644 --- a/agent/tcf/framework/mdep.h +++ b/agent/tcf/framework/mdep.h @@ -397,7 +397,14 @@ extern const char * get_user_name(void); extern const char * create_uuid(void); /* Switch to running in the background, rather than under the direct control of a user */ +#if defined(_WIN32) +extern void become_daemon(char ** argv); +#else extern void become_daemon(void); +#endif + +/* Close stdout and stderr and replace with null output device */ +extern void close_out_and_err(void); /* Return 1 if running in the background, return 0 othewise */ extern int is_daemon(void); diff --git a/agent/tcf/framework/trace.c b/agent/tcf/framework/trace.c index e9095495..1f113a44 100644 --- a/agent/tcf/framework/trace.c +++ b/agent/tcf/framework/trace.c @@ -34,6 +34,8 @@ int log_mode = LOG_EVENTS | LOG_CHILD | LOG_WAITPID | LOG_CONTEXT | LOG_PROTOCOL #include <syslog.h> #endif +static int use_syslog = 0; + FILE * log_file = NULL; #define MAX_TRACE_MODES 40 @@ -67,7 +69,7 @@ int print_trace(int mode, const char * fmt, ...) { if (mode != LOG_ALWAYS && (log_mode & mode) == 0) return 0; va_start(ap, fmt); - if (is_daemon()) { + if (use_syslog) { #if defined(_WIN32) #elif defined(_WRS_KERNEL) #elif defined(__SYMBIAN32__) @@ -175,6 +177,15 @@ void open_log_file(const char * log_name) { } else if (strcmp(log_name, LOG_NAME_STDERR) == 0) { log_file = stderr; + if (is_daemon()) { +#if defined(_WIN32) +#elif defined(_WRS_KERNEL) +#elif defined(__SYMBIAN32__) +#else + use_syslog = 1; + openlog("tcf-agent", LOG_PID, LOG_DAEMON); +#endif + } } else if ((log_file = fopen(log_name, "a")) == NULL) { fprintf(stderr, "TCF: error: cannot create log file %s\n", log_name); diff --git a/agent/tcf/main/main.c b/agent/tcf/main/main.c index e3a56619..c43bf3b9 100644 --- a/agent/tcf/main/main.c +++ b/agent/tcf/main/main.c @@ -195,7 +195,7 @@ int main(int argc, char ** argv) { /* Parse arguments */ for (ind = 1; ind < argc; ind++) { - const char * s = argv[ind]; + char * s = argv[ind]; if (*s != '-') { break; } @@ -214,6 +214,17 @@ int main(int argc, char ** argv) { break; case 'd': +#if defined(_WIN32) + /* For Windows the only way to detach a process is to + * create a new process, so we patch the -d option to + * -D for the second time we get invoked so we don't + * keep on creating new processes forever. */ + s[-1] = 'D'; + daemon = 2; + break; + + case 'D': +#endif daemon = 1; break; @@ -279,13 +290,13 @@ int main(int argc, char ** argv) { } } - if (daemon && log_name != NULL && strcmp (log_name, LOG_NAME_STDERR) != 0) { - fprintf(stderr, "%s: error: can only log to stderr when in daemon " - "mode.\n", progname); - exit (1); + if (daemon) { +#if defined(_WIN32) + become_daemon(daemon > 1 ? argv : NULL); +#else + become_daemon(); +#endif } - - if (daemon) become_daemon(); open_log_file(log_name); #endif @@ -329,6 +340,9 @@ int main(int argc, char ** argv) { loc_free(server_properties); } + if (daemon) + close_out_and_err(); + #if ENABLE_SignalHandlers signal(SIGABRT, signal_handler); signal(SIGILL, signal_handler); |