#include <fcntl.h>
#include <paths.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <CoreFoundation/CoreFoundation.h>
#include <SystemConfiguration/SCPrivate.h>
#include "SCHelper_client.h"
#include "helper_comm.h"
#define HELPER "SCHelper"
#define HELPER_LEN (sizeof(HELPER) - 1)
#define SUFFIX_SYM "~sym"
#define SUFFIX_SYM_LEN (sizeof(SUFFIX_SYM) - 1)
__private_extern__
int
_SCHelperOpen(CFDataRef authorizationData)
{
sigset_t block;
sigset_t block_old;
CFBundleRef bundle;
int comm[2] = { -1, -1 };
int exit_status = 0;
struct sigaction ignore;
struct sigaction int_old;
Boolean ok = FALSE;
char path[MAXPATHLEN]= { 0 };
pid_t pid1;
struct sigaction quit_old;
uint32_t status = 0;
CFURLRef url = NULL;
static int yes = 1;
bundle = _SC_CFBundleGet();
if (bundle != NULL) {
url = CFBundleCopyResourceURL(bundle, CFSTR(HELPER), NULL, NULL);
}
if (url != NULL) {
if (!CFURLGetFileSystemRepresentation(url, TRUE, (UInt8 *)path, sizeof(path))) {
path[0] = 0;
}
CFRelease(url);
}
if (socketpair(AF_UNIX, SOCK_STREAM, 0, comm) == -1) {
perror("_SCHelperOpen socketpair() failed");
return -1;
}
ignore.sa_handler = SIG_IGN;
ignore.sa_flags = 0;
(void)sigemptyset(&ignore.sa_mask);
(void)sigaction(SIGINT , &ignore, &int_old );
(void)sigaction(SIGQUIT, &ignore, &quit_old);
(void)sigemptyset(&block);
(void)sigaddset(&block, SIGCHLD);
(void)sigprocmask(SIG_BLOCK, &block, &block_old);
pid1 = fork();
if (pid1 == -1) { perror("_SCHelperOpen fork() failed");
goto done;
} else if (pid1 == 0) { int i;
pid_t pid2;
closelog();
if (comm[0] != STDIN_FILENO) {
(void)dup2(comm[0], STDIN_FILENO);
}
if (comm[0] != STDOUT_FILENO) {
(void)dup2(comm[0], STDOUT_FILENO);
}
(void)close(STDERR_FILENO);
(void)open(_PATH_CONSOLE, O_WRONLY, 0);
for (i = getdtablesize() - 1; i > STDERR_FILENO; i--) {
(void)close(i);
}
pid2 = vfork();
if (pid2 == -1) { int err = errno;
perror("_SCHelperOpen vfork() failed\n");
(void)__SCHelper_txMessage(STDOUT_FILENO, err, NULL);
_exit(err);
} else if (pid2 == 0) { char *env;
int err = ENOENT;
size_t len;
(void)sigaction(SIGINT , &int_old , NULL);
(void)sigaction(SIGQUIT, &quit_old, NULL);
(void)sigprocmask(SIG_SETMASK, &block_old, NULL);
if (path[0] != 0) {
(void)execl(path, path, NULL);
err = errno;
}
env = getenv("DYLD_FRAMEWORK_PATH");
len = (env != NULL) ? strlen(env) : 0;
while ((len > 1) && (env[len - 1] == '/')) {
len--;
}
if ((len > SUFFIX_SYM_LEN) &&
(strncmp(&env[len - SUFFIX_SYM_LEN], SUFFIX_SYM, SUFFIX_SYM_LEN) == 0) &&
((len + 1 + HELPER_LEN) < MAXPATHLEN)) {
char path[MAXPATHLEN];
strlcpy(path, env, sizeof(path));
strlcpy(&path[len], "/", sizeof(path) - (len - 1));
strlcat(&path[len], HELPER, sizeof(path) - len);
(void)execl(path, path, NULL);
err = errno;
}
(void)__SCHelper_txMessage(STDOUT_FILENO, err, NULL);
_exit(err != 0 ? err : ENOENT);
}
_exit(0);
}
if (wait4(pid1, &exit_status, 0, NULL) == -1) {
perror("_SCHelperOpen wait4() failed");
goto done;
}
if (WIFEXITED(exit_status)) {
if (WEXITSTATUS(exit_status) != 0) {
SCLog(TRUE, LOG_INFO,
CFSTR("could not start \"" HELPER "[1]\", exited w/status = %d"),
WEXITSTATUS(exit_status));
goto done;
}
} else if (WIFSIGNALED(exit_status)) {
SCLog(TRUE, LOG_INFO,
CFSTR("could not start \"" HELPER "[1]\", terminated w/signal = %d"),
WTERMSIG(exit_status));
goto done;
} else {
SCLog(TRUE, LOG_INFO,
CFSTR("could not start \"" HELPER "[1]\", exit_status = %x"),
exit_status);
goto done;
}
(void)close(comm[0]);
comm[0] = -1;
if (setsockopt(comm[1], SOL_SOCKET, SO_NOSIGPIPE, (const void *)&yes, sizeof(yes)) == -1) {
perror("_SCHelperOpen setsockopt() failed");
goto done;
}
ok = __SCHelper_rxMessage(comm[1], &status, NULL);
if (!ok) {
SCLog(TRUE, LOG_INFO, CFSTR("could not start \"" HELPER "\", no status available"));
goto done;
}
ok = (status == 0);
if (!ok) {
SCLog(TRUE, LOG_INFO, CFSTR("could not start \"" HELPER "\", status = %u"), status);
goto done;
}
ok = _SCHelperExec(comm[1], SCHELPER_MSG_AUTH, authorizationData, &status, NULL);
if (!ok) {
SCLog(TRUE, LOG_INFO, CFSTR("_SCHelperOpen: could not send authorization"));
goto done;
}
done :
(void)sigaction(SIGINT , &int_old , NULL);
(void)sigaction(SIGQUIT, &quit_old, NULL);
(void)sigprocmask(SIG_SETMASK, &block_old, NULL);
if (comm[0] > 0) {
(void)close(comm[0]);
}
if (!ok) {
(void)close(comm[1]);
comm[1] = -1;
}
return comm[1];
}
__private_extern__
void
_SCHelperClose(int helper)
{
if (!_SCHelperExec(helper, SCHELPER_MSG_EXIT, NULL, NULL, NULL)) {
SCLog(TRUE, LOG_INFO, CFSTR("_SCHelperOpen: could not send exit request"));
}
(void)close(helper);
return;
}