int doesMatchP2(unsigned char c, unsigned short *vals,int *nsamples) { #define P2CHAN 6 enum P2State { waitingA5, waiting5A, waitingVersion, waitingCounter, waitingData, waitingSwitches }; static int packetCounter = 0; static int chan = 0; static int i = 0; static enum P2State state = waitingA5; static unsigned short s[MAXCHANNELS]; switch (state) { case waitingA5: if (c == 0xa5) { failCount = 0; state = waiting5A; } else { failCount += 1; if (failCount % PROTOWINDOW == 0) { rprintf("Sync error, packet lost.\n"); } } break; case waiting5A: if (c == 0x5a) state = waitingVersion; else state = waitingA5; break; case waitingVersion: if (c == 0x02) /* only version 2 supported right now */ state = waitingCounter; else state = waitingA5; break; case waitingCounter: packetCounter = c; state = waitingData; i = 0; break; case waitingData: if ((i%2) == 0) s[i/2] = c; else s[i/2] = s[i/2]* 256 + c; i += 1; if (i == P2CHAN * 2) state = waitingSwitches; break; case waitingSwitches: chan = P2CHAN; chan = 2; /* todo: fix me */ *nsamples = chan; memcpy(vals, s, chan * sizeof(s[0])); handleSamples(packetCounter, *nsamples, vals); state = waitingA5; return 1; default: rprintf("Error: unknown state in P2!\n"); rexit(0); } return 0; }
int main(int argc, char **argv) { char responseBuf[MAXLEN]; int i; int retval; ser_t serport; char EDFPacket[MAXHEADERLEN]; int EDFLen = MAXHEADERLEN; /// buffer for reading from serial port char smallbuf[PROTOWINDOW]; char *hostname = NULL; char *deviceName = NULL; /// DEFAULTHOST; unsigned short portno = 0; /// DEFAULTPORT; struct timeval when; rprintf("ModEEG Driver v. %s-%s\n", VERSION, OSTYPESTR); rinitNetworking(); /// process command line inputs for (i = 1; argv[i]; ++i) { if (argv[i][0] == '-' && argv[i][1] == 'h') { printHelp(); exit(0); } if (argv[i][0] == '-' && argv[i][1] == 'd') { if (argv[i+1] == NULL) { rprintf("Bad devicename option: %s\nExpected device name as next argument.\n", argv[i]); exit(1); } deviceName = argv[i+1]; i += 1; continue; } if (hostname == NULL) { hostname = argv[i]; continue; } if (portno == 0) { portno = atoi(argv[i]); continue; } } if (deviceName == NULL) deviceName = DEFAULTDEVICE; if (portno == 0) portno = DEFAULTPORT; if (hostname == NULL) hostname = DEFAULTHOST; makeREDFConfig(¤t, &modEEGCfg); writeEDFString(¤t, EDFPacket, &EDFLen); sock_fd = rsocket(); if (sock_fd < 0) { perror("socket"); rexit(1); } setblocking(sock_fd); rprintf("Attempting to connect to nsd at %s:%d\n", hostname, portno); retval = rconnectName(sock_fd, hostname, portno); if (retval != 0) { rprintf("connect error\n"); rexit(1); } rprintf("Socket connected.\n"); fflush(stdout); serport = openSerialPort(deviceName,57600); rprintf("Serial port %s opened.\n", deviceName); writeString(sock_fd, "eeg\n", &ob); mGetOK(sock_fd, &ib); writeString(sock_fd, "setheader ", &ob); writeBytes(sock_fd, EDFPacket, EDFLen, &ob); writeString(sock_fd, "\n", &ob); mGetOK(sock_fd, &ib); updateMaxFd(sock_fd); #ifndef __MINGW32__ updateMaxFd(serport); #endif when.tv_usec = (1000000L / modEEGCfg.chan[0].sampleCount); rprintf("Polling at %d Hertz or %d usec\n", modEEGCfg.chan[0].sampleCount, when.tv_usec); for (;;) { int i, readSerBytes; int retval; fd_set toread; when.tv_sec = 0; when.tv_usec = (1000000L / modEEGCfg.chan[0].sampleCount); FD_ZERO(&toread); FD_SET(sock_fd, &toread); #ifndef __MINGW32__ FD_SET(serport, &toread); #endif retval = rselect_timed(max_fd, &toread, NULL, NULL, &when); readSerBytes = readSerial(serport, smallbuf, PROTOWINDOW); if (readSerBytes < 0) { rprintf("Serial error.\n"); } if (readSerBytes > 0) { for (i = 0; i < readSerBytes; ++i) eatCharacter(smallbuf[i]); } if (FD_ISSET(sock_fd, &toread)) { my_read(sock_fd, responseBuf, MAXLEN, &ib); } if (isEOF(sock_fd, &ib)) { rprintf("Server died, exitting.\n"); exit(0); } } return 0; }
int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_function, void* exec_payload, lxc_attach_options_t* options, pid_t* attached_process) { int ret, status; pid_t init_pid, pid, attached_pid, expected; struct lxc_proc_context_info *init_ctx; char* cwd; char* new_cwd; int ipc_sockets[2]; signed long personality; if (!options) options = &attach_static_default_options; init_pid = lxc_cmd_get_init_pid(name, lxcpath); if (init_pid < 0) { ERROR("Failed to get init pid."); return -1; } init_ctx = lxc_proc_get_context_info(init_pid); if (!init_ctx) { ERROR("Failed to get context of init process: %ld.", (long)init_pid); return -1; } personality = get_personality(name, lxcpath); if (init_ctx->personality < 0) { ERROR("Failed to get personality of the container."); lxc_proc_put_context_info(init_ctx); return -1; } init_ctx->personality = personality; init_ctx->container = lxc_container_new(name, lxcpath); if (!init_ctx->container) return -1; if (!fetch_seccomp(init_ctx->container, options)) WARN("Failed to get seccomp policy."); if (!no_new_privs(init_ctx->container, options)) WARN("Could not determine whether PR_SET_NO_NEW_PRIVS is set."); cwd = getcwd(NULL, 0); /* Determine which namespaces the container was created with * by asking lxc-start, if necessary. */ if (options->namespaces == -1) { options->namespaces = lxc_cmd_get_clone_flags(name, lxcpath); /* call failed */ if (options->namespaces == -1) { ERROR("Failed to automatically determine the " "namespaces which the container uses."); free(cwd); lxc_proc_put_context_info(init_ctx); return -1; } } /* Create a socket pair for IPC communication; set SOCK_CLOEXEC in order * to make sure we don't irritate other threads that want to fork+exec * away * * IMPORTANT: if the initial process is multithreaded and another call * just fork()s away without exec'ing directly after, the socket fd will * exist in the forked process from the other thread and any close() in * our own child process will not really cause the socket to close * properly, potentiall causing the parent to hang. * * For this reason, while IPC is still active, we have to use shutdown() * if the child exits prematurely in order to signal that the socket is * closed and cannot assume that the child exiting will automatically do * that. * * IPC mechanism: (X is receiver) * initial process intermediate attached * X <--- send pid of * attached proc, * then exit * send 0 ------------------------------------> X * [do initialization] * X <------------------------------------ send 1 * [add to cgroup, ...] * send 2 ------------------------------------> X * [set LXC_ATTACH_NO_NEW_PRIVS] * X <------------------------------------ send 3 * [open LSM label fd] * send 4 ------------------------------------> X * [set LSM label] * close socket close socket * run program */ ret = socketpair(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets); if (ret < 0) { SYSERROR("Could not set up required IPC mechanism for attaching."); free(cwd); lxc_proc_put_context_info(init_ctx); return -1; } /* Create intermediate subprocess, three reasons: * 1. Runs all pthread_atfork handlers and the child will no * longer be threaded (we can't properly setns() in a threaded * process). * 2. We can't setns() in the child itself, since we want to make * sure we are properly attached to the pidns. * 3. Also, the initial thread has to put the attached process * into the cgroup, which we can only do if we didn't already * setns() (otherwise, user namespaces will hate us). */ pid = fork(); if (pid < 0) { SYSERROR("Failed to create first subprocess."); free(cwd); lxc_proc_put_context_info(init_ctx); return -1; } if (pid) { int procfd = -1; pid_t to_cleanup_pid = pid; /* Initial thread, we close the socket that is for the * subprocesses. */ close(ipc_sockets[1]); free(cwd); /* Attach to cgroup, if requested. */ if (options->attach_flags & LXC_ATTACH_MOVE_TO_CGROUP) { if (!cgroup_attach(name, lxcpath, pid)) goto on_error; } /* Setup resource limits */ if (!lxc_list_empty(&init_ctx->container->lxc_conf->limits) && setup_resource_limits(&init_ctx->container->lxc_conf->limits, pid)) { goto on_error; } /* Open /proc before setns() to the containers namespace so we * don't rely on any information from inside the container. */ procfd = open("/proc", O_DIRECTORY | O_RDONLY | O_CLOEXEC); if (procfd < 0) { SYSERROR("Unable to open /proc."); goto on_error; } /* Let the child process know to go ahead. */ status = 0; ret = lxc_write_nointr(ipc_sockets[0], &status, sizeof(status)); if (ret <= 0) { ERROR("Intended to send sequence number 0: %s.", strerror(errno)); goto on_error; } /* Get pid of attached process from intermediate process. */ ret = lxc_read_nointr_expect(ipc_sockets[0], &attached_pid, sizeof(attached_pid), NULL); if (ret <= 0) { if (ret != 0) ERROR("Expected to receive pid: %s.", strerror(errno)); goto on_error; } /* Ignore SIGKILL (CTRL-C) and SIGQUIT (CTRL-\) - issue #313. */ if (options->stdin_fd == 0) { signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); } /* Reap intermediate process. */ ret = wait_for_pid(pid); if (ret < 0) goto on_error; /* We will always have to reap the attached process now. */ to_cleanup_pid = attached_pid; /* Tell attached process it may start initializing. */ status = 0; ret = lxc_write_nointr(ipc_sockets[0], &status, sizeof(status)); if (ret <= 0) { ERROR("Intended to send sequence number 0: %s.", strerror(errno)); goto on_error; } /* Wait for the attached process to finish initializing. */ expected = 1; ret = lxc_read_nointr_expect(ipc_sockets[0], &status, sizeof(status), &expected); if (ret <= 0) { if (ret != 0) ERROR("Expected to receive sequence number 1: %s.", strerror(errno)); goto on_error; } /* Tell attached process we're done. */ status = 2; ret = lxc_write_nointr(ipc_sockets[0], &status, sizeof(status)); if (ret <= 0) { ERROR("Intended to send sequence number 2: %s.", strerror(errno)); goto on_error; } /* Wait for the (grand)child to tell us that it's ready to set * up its LSM labels. */ expected = 3; ret = lxc_read_nointr_expect(ipc_sockets[0], &status, sizeof(status), &expected); if (ret <= 0) { ERROR("Expected to receive sequence number 3: %s.", strerror(errno)); goto on_error; } /* Open LSM fd and send it to child. */ if ((options->namespaces & CLONE_NEWNS) && (options->attach_flags & LXC_ATTACH_LSM) && init_ctx->lsm_label) { int on_exec, saved_errno; int labelfd = -1; on_exec = options->attach_flags & LXC_ATTACH_LSM_EXEC ? 1 : 0; /* Open fd for the LSM security module. */ labelfd = lsm_openat(procfd, attached_pid, on_exec); if (labelfd < 0) goto on_error; /* Send child fd of the LSM security module to write to. */ ret = lxc_abstract_unix_send_fds(ipc_sockets[0], &labelfd, 1, NULL, 0); saved_errno = errno; close(labelfd); if (ret <= 0) { ERROR("Intended to send file descriptor %d: %s.", labelfd, strerror(saved_errno)); goto on_error; } } if (procfd >= 0) close(procfd); /* Now shut down communication with child, we're done. */ shutdown(ipc_sockets[0], SHUT_RDWR); close(ipc_sockets[0]); lxc_proc_put_context_info(init_ctx); /* We're done, the child process should now execute whatever it * is that the user requested. The parent can now track it with * waitpid() or similar. */ *attached_process = attached_pid; return 0; on_error: /* First shut down the socket, then wait for the pid, otherwise * the pid we're waiting for may never exit. */ if (procfd >= 0) close(procfd); shutdown(ipc_sockets[0], SHUT_RDWR); close(ipc_sockets[0]); if (to_cleanup_pid) (void) wait_for_pid(to_cleanup_pid); lxc_proc_put_context_info(init_ctx); return -1; } /* First subprocess begins here, we close the socket that is for the * initial thread. */ close(ipc_sockets[0]); /* Wait for the parent to have setup cgroups. */ expected = 0; status = -1; ret = lxc_read_nointr_expect(ipc_sockets[1], &status, sizeof(status), &expected); if (ret <= 0) { ERROR("Expected to receive sequence number 0: %s.", strerror(errno)); shutdown(ipc_sockets[1], SHUT_RDWR); rexit(-1); } if ((options->attach_flags & LXC_ATTACH_MOVE_TO_CGROUP) && cgns_supported()) options->namespaces |= CLONE_NEWCGROUP; /* Attach now, create another subprocess later, since pid namespaces * only really affect the children of the current process. */ ret = lxc_attach_to_ns(init_pid, options->namespaces); if (ret < 0) { ERROR("Failed to enter namespaces."); shutdown(ipc_sockets[1], SHUT_RDWR); rexit(-1); } /* Attach succeeded, try to cwd. */ if (options->initial_cwd) new_cwd = options->initial_cwd; else new_cwd = cwd; ret = chdir(new_cwd); if (ret < 0) WARN("Could not change directory to \"%s\".", new_cwd); free(cwd); /* Now create the real child process. */ { struct attach_clone_payload payload = { .ipc_socket = ipc_sockets[1], .options = options, .init_ctx = init_ctx, .exec_function = exec_function, .exec_payload = exec_payload, }; /* We use clone_parent here to make this subprocess a direct * child of the initial process. Then this intermediate process * can exit and the parent can directly track the attached * process. */ pid = lxc_clone(attach_child_main, &payload, CLONE_PARENT); } /* Shouldn't happen, clone() should always return positive pid. */ if (pid <= 0) { SYSERROR("Failed to create subprocess."); shutdown(ipc_sockets[1], SHUT_RDWR); rexit(-1); } /* Tell grandparent the pid of the pid of the newly created child. */ ret = lxc_write_nointr(ipc_sockets[1], &pid, sizeof(pid)); if (ret != sizeof(pid)) { /* If this really happens here, this is very unfortunate, since * the parent will not know the pid of the attached process and * will not be able to wait for it (and we won't either due to * CLONE_PARENT) so the parent won't be able to reap it and the * attached process will remain a zombie. */ ERROR("Intended to send pid %d: %s.", pid, strerror(errno)); shutdown(ipc_sockets[1], SHUT_RDWR); rexit(-1); } /* The rest is in the hands of the initial and the attached process. */ rexit(0); }
static int attach_child_main(void* data) { struct attach_clone_payload* payload = (struct attach_clone_payload*)data; int ipc_socket = payload->ipc_socket; lxc_attach_options_t* options = payload->options; struct lxc_proc_context_info* init_ctx = payload->init_ctx; #if HAVE_SYS_PERSONALITY_H long new_personality; #endif int ret; int status; int expected; long flags; int fd; int lsm_labelfd; uid_t new_uid; gid_t new_gid; /* Wait for the initial thread to signal us that it's ready for us to * start initializing. */ expected = 0; status = -1; ret = lxc_read_nointr_expect(ipc_socket, &status, sizeof(status), &expected); if (ret <= 0) { ERROR("Expected to receive sequence number 0: %s.", strerror(errno)); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } /* A description of the purpose of this functionality is provided in the * lxc-attach(1) manual page. We have to remount here and not in the * parent process, otherwise /proc may not properly reflect the new pid * namespace. */ if (!(options->namespaces & CLONE_NEWNS) && (options->attach_flags & LXC_ATTACH_REMOUNT_PROC_SYS)) { ret = lxc_attach_remount_sys_proc(); if (ret < 0) { shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } } /* Now perform additional attachments. */ #if HAVE_SYS_PERSONALITY_H if (options->personality < 0) new_personality = init_ctx->personality; else new_personality = options->personality; if (options->attach_flags & LXC_ATTACH_SET_PERSONALITY) { ret = personality(new_personality); if (ret < 0) { SYSERROR("Could not ensure correct architecture."); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } } #endif if (options->attach_flags & LXC_ATTACH_DROP_CAPABILITIES) { ret = lxc_attach_drop_privs(init_ctx); if (ret < 0) { ERROR("Could not drop privileges."); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } } /* Always set the environment (specify (LXC_ATTACH_KEEP_ENV, NULL, NULL) * if you want this to be a no-op). */ ret = lxc_attach_set_environment(options->env_policy, options->extra_env_vars, options->extra_keep_env); if (ret < 0) { ERROR("Could not set initial environment for attached process."); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } /* Set {u,g}id. */ new_uid = 0; new_gid = 0; /* Ignore errors, we will fall back to root in that case (/proc was not * mounted etc.). */ if (options->namespaces & CLONE_NEWUSER) lxc_attach_get_init_uidgid(&new_uid, &new_gid); if (options->uid != (uid_t)-1) new_uid = options->uid; if (options->gid != (gid_t)-1) new_gid = options->gid; /* Setup the controlling tty. */ if (options->stdin_fd && isatty(options->stdin_fd)) { if (setsid() < 0) { SYSERROR("Unable to setsid."); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } if (ioctl(options->stdin_fd, TIOCSCTTY, (char *)NULL) < 0) { SYSERROR("Unable to set TIOCSTTY."); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } } /* Try to set the {u,g}id combination. */ if ((new_gid != 0 || options->namespaces & CLONE_NEWUSER)) { if (setgid(new_gid) || setgroups(0, NULL)) { SYSERROR("Switching to container gid."); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } } if ((new_uid != 0 || options->namespaces & CLONE_NEWUSER) && setuid(new_uid)) { SYSERROR("Switching to container uid."); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } /* Tell initial process it may now put us into cgroups. */ status = 1; ret = lxc_write_nointr(ipc_socket, &status, sizeof(status)); if (ret != sizeof(status)) { ERROR("Intended to send sequence number 1: %s.", strerror(errno)); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } /* Wait for the initial thread to signal us that it has done everything * for us when it comes to cgroups etc. */ expected = 2; status = -1; ret = lxc_read_nointr_expect(ipc_socket, &status, sizeof(status), &expected); if (ret <= 0) { ERROR("Expected to receive sequence number 2: %s", strerror(errno)); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } if ((init_ctx->container && init_ctx->container->lxc_conf && init_ctx->container->lxc_conf->no_new_privs) || (options->attach_flags & LXC_ATTACH_NO_NEW_PRIVS)) { if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) { SYSERROR("PR_SET_NO_NEW_PRIVS could not be set. " "Process can use execve() gainable " "privileges."); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } INFO("PR_SET_NO_NEW_PRIVS is set. Process cannot use execve() " "gainable privileges."); } /* Tell the (grand)parent to send us LSM label fd. */ status = 3; ret = lxc_write_nointr(ipc_socket, &status, sizeof(status)); if (ret <= 0) { ERROR("Intended to send sequence number 3: %s.", strerror(errno)); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } if ((options->namespaces & CLONE_NEWNS) && (options->attach_flags & LXC_ATTACH_LSM) && init_ctx->lsm_label) { int on_exec; /* Receive fd for LSM security module. */ ret = lxc_abstract_unix_recv_fds(ipc_socket, &lsm_labelfd, 1, NULL, 0); if (ret <= 0) { ERROR("Expected to receive file descriptor: %s.", strerror(errno)); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } /* Change into our new LSM profile. */ on_exec = options->attach_flags & LXC_ATTACH_LSM_EXEC ? 1 : 0; if (lsm_set_label_at(lsm_labelfd, on_exec, init_ctx->lsm_label) < 0) { SYSERROR("Failed to set LSM label."); shutdown(ipc_socket, SHUT_RDWR); close(lsm_labelfd); rexit(-1); } close(lsm_labelfd); } if (init_ctx->container && init_ctx->container->lxc_conf && init_ctx->container->lxc_conf->seccomp && (lxc_seccomp_load(init_ctx->container->lxc_conf) != 0)) { ERROR("Failed to load seccomp policy."); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } shutdown(ipc_socket, SHUT_RDWR); close(ipc_socket); lxc_proc_put_context_info(init_ctx); /* The following is done after the communication socket is shut down. * That way, all errors that might (though unlikely) occur up until this * point will have their messages printed to the original stderr (if * logging is so configured) and not the fd the user supplied, if any. */ /* Fd handling for stdin, stdout and stderr; ignore errors here, user * may want to make sure the fds are closed, for example. */ if (options->stdin_fd >= 0 && options->stdin_fd != 0) dup2(options->stdin_fd, 0); if (options->stdout_fd >= 0 && options->stdout_fd != 1) dup2(options->stdout_fd, 1); if (options->stderr_fd >= 0 && options->stderr_fd != 2) dup2(options->stderr_fd, 2); /* close the old fds */ if (options->stdin_fd > 2) close(options->stdin_fd); if (options->stdout_fd > 2) close(options->stdout_fd); if (options->stderr_fd > 2) close(options->stderr_fd); /* Try to remove FD_CLOEXEC flag from stdin/stdout/stderr, but also * here, ignore errors. */ for (fd = 0; fd <= 2; fd++) { flags = fcntl(fd, F_GETFL); if (flags < 0) continue; if (flags & FD_CLOEXEC) if (fcntl(fd, F_SETFL, flags & ~FD_CLOEXEC) < 0) SYSERROR("Unable to clear FD_CLOEXEC from file descriptor."); } /* We're done, so we can now do whatever the user intended us to do. */ rexit(payload->exec_function(payload->exec_payload)); }
int main(int argc, char ** argv) { int * status = malloc(sizeof(int)); char * exeName = "/bin/cat" ; int i; struct stat results ; char * buffer ; int pid[10]; if (argc > 1) exeName = argv[1] ; FILE * fp = fopen(exeName, "rb") ; if (!fp) { fputs ("File error",stderr) ; exit (1) ; } if (stat(exeName, &results) != 0) { fputs ("File error", stderr) ; exit (1) ; } printf ("Exe size: %ld\n", results.st_size) ; buffer = (char*) malloc (sizeof(char)*results.st_size) ; if (!buffer) { fputs ("Memory error", stderr) ; exit (2); } if (fread (buffer, 1, results.st_size, fp) != results.st_size) { fputs ("Reading error",stderr); exit (1); } for (i=0 ; i<10 ; i++) { pid[i] = rexecut("localhost", buffer, results.st_size) ; printf("Executing process on remote server ...\n"); printf("==> Rexecut result : %d\n\n", pid[i]); } for (i=0 ; i<10 ; i++) { printf("Sending signal %d to process (%d) ...\n", 10-i, pid[i]); printf("==> Rkill result : %d\n\n", rkill(pid[i], 10-i)); } printf("Exiting all processes with status 15 ...\n"); printf("==> Rexit result : %d\n", rexit(15)) ; free (status) ; fclose (fp); return 0 ; }
int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_function, void* exec_payload, lxc_attach_options_t* options, pid_t* attached_process) { int ret, status; pid_t init_pid, pid, attached_pid, expected; struct lxc_proc_context_info *init_ctx; char* cwd; char* new_cwd; int ipc_sockets[2]; int procfd; signed long personality; if (!options) options = &attach_static_default_options; init_pid = lxc_cmd_get_init_pid(name, lxcpath); if (init_pid < 0) { ERROR("failed to get the init pid"); return -1; } init_ctx = lxc_proc_get_context_info(init_pid); if (!init_ctx) { ERROR("failed to get context of the init process, pid = %ld", (long)init_pid); return -1; } personality = get_personality(name, lxcpath); if (init_ctx->personality < 0) { ERROR("Failed to get personality of the container"); lxc_proc_put_context_info(init_ctx); return -1; } init_ctx->personality = personality; if (!fetch_seccomp(name, lxcpath, init_ctx, options)) WARN("Failed to get seccomp policy"); cwd = getcwd(NULL, 0); /* determine which namespaces the container was created with * by asking lxc-start, if necessary */ if (options->namespaces == -1) { options->namespaces = lxc_cmd_get_clone_flags(name, lxcpath); /* call failed */ if (options->namespaces == -1) { ERROR("failed to automatically determine the " "namespaces which the container unshared"); free(cwd); lxc_proc_put_context_info(init_ctx); return -1; } } /* create a socket pair for IPC communication; set SOCK_CLOEXEC in order * to make sure we don't irritate other threads that want to fork+exec away * * IMPORTANT: if the initial process is multithreaded and another call * just fork()s away without exec'ing directly after, the socket fd will * exist in the forked process from the other thread and any close() in * our own child process will not really cause the socket to close properly, * potentiall causing the parent to hang. * * For this reason, while IPC is still active, we have to use shutdown() * if the child exits prematurely in order to signal that the socket * is closed and cannot assume that the child exiting will automatically * do that. * * IPC mechanism: (X is receiver) * initial process intermediate attached * X <--- send pid of * attached proc, * then exit * send 0 ------------------------------------> X * [do initialization] * X <------------------------------------ send 1 * [add to cgroup, ...] * send 2 ------------------------------------> X * close socket close socket * run program */ ret = socketpair(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets); if (ret < 0) { SYSERROR("could not set up required IPC mechanism for attaching"); free(cwd); lxc_proc_put_context_info(init_ctx); return -1; } /* create intermediate subprocess, three reasons: * 1. runs all pthread_atfork handlers and the * child will no longer be threaded * (we can't properly setns() in a threaded process) * 2. we can't setns() in the child itself, since * we want to make sure we are properly attached to * the pidns * 3. also, the initial thread has to put the attached * process into the cgroup, which we can only do if * we didn't already setns() (otherwise, user * namespaces will hate us) */ pid = fork(); if (pid < 0) { SYSERROR("failed to create first subprocess"); free(cwd); lxc_proc_put_context_info(init_ctx); return -1; } if (pid) { pid_t to_cleanup_pid = pid; /* initial thread, we close the socket that is for the * subprocesses */ close(ipc_sockets[1]); free(cwd); /* attach to cgroup, if requested */ if (options->attach_flags & LXC_ATTACH_MOVE_TO_CGROUP) { if (!cgroup_attach(name, lxcpath, pid)) goto cleanup_error; } /* Let the child process know to go ahead */ status = 0; ret = lxc_write_nointr(ipc_sockets[0], &status, sizeof(status)); if (ret <= 0) { ERROR("error using IPC to notify attached process for initialization (0)"); goto cleanup_error; } /* get pid from intermediate process */ ret = lxc_read_nointr_expect(ipc_sockets[0], &attached_pid, sizeof(attached_pid), NULL); if (ret <= 0) { if (ret != 0) ERROR("error using IPC to receive pid of attached process"); goto cleanup_error; } /* ignore SIGKILL (CTRL-C) and SIGQUIT (CTRL-\) - issue #313 */ if (options->stdin_fd == 0) { signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); } /* reap intermediate process */ ret = wait_for_pid(pid); if (ret < 0) goto cleanup_error; /* we will always have to reap the grandchild now */ to_cleanup_pid = attached_pid; /* tell attached process it may start initializing */ status = 0; ret = lxc_write_nointr(ipc_sockets[0], &status, sizeof(status)); if (ret <= 0) { ERROR("error using IPC to notify attached process for initialization (0)"); goto cleanup_error; } /* wait for the attached process to finish initializing */ expected = 1; ret = lxc_read_nointr_expect(ipc_sockets[0], &status, sizeof(status), &expected); if (ret <= 0) { if (ret != 0) ERROR("error using IPC to receive notification from attached process (1)"); goto cleanup_error; } /* tell attached process we're done */ status = 2; ret = lxc_write_nointr(ipc_sockets[0], &status, sizeof(status)); if (ret <= 0) { ERROR("error using IPC to notify attached process for initialization (2)"); goto cleanup_error; } /* now shut down communication with child, we're done */ shutdown(ipc_sockets[0], SHUT_RDWR); close(ipc_sockets[0]); lxc_proc_put_context_info(init_ctx); /* we're done, the child process should now execute whatever * it is that the user requested. The parent can now track it * with waitpid() or similar. */ *attached_process = attached_pid; return 0; cleanup_error: /* first shut down the socket, then wait for the pid, * otherwise the pid we're waiting for may never exit */ shutdown(ipc_sockets[0], SHUT_RDWR); close(ipc_sockets[0]); if (to_cleanup_pid) (void) wait_for_pid(to_cleanup_pid); lxc_proc_put_context_info(init_ctx); return -1; } /* first subprocess begins here, we close the socket that is for the * initial thread */ close(ipc_sockets[0]); /* Wait for the parent to have setup cgroups */ expected = 0; status = -1; ret = lxc_read_nointr_expect(ipc_sockets[1], &status, sizeof(status), &expected); if (ret <= 0) { ERROR("error communicating with child process"); shutdown(ipc_sockets[1], SHUT_RDWR); rexit(-1); } if ((options->attach_flags & LXC_ATTACH_MOVE_TO_CGROUP) && cgns_supported()) options->namespaces |= CLONE_NEWCGROUP; procfd = open("/proc", O_DIRECTORY | O_RDONLY); if (procfd < 0) { SYSERROR("Unable to open /proc"); shutdown(ipc_sockets[1], SHUT_RDWR); rexit(-1); } /* attach now, create another subprocess later, since pid namespaces * only really affect the children of the current process */ ret = lxc_attach_to_ns(init_pid, options->namespaces); if (ret < 0) { ERROR("failed to enter the namespace"); shutdown(ipc_sockets[1], SHUT_RDWR); rexit(-1); } /* attach succeeded, try to cwd */ if (options->initial_cwd) new_cwd = options->initial_cwd; else new_cwd = cwd; ret = chdir(new_cwd); if (ret < 0) WARN("could not change directory to '%s'", new_cwd); free(cwd); /* now create the real child process */ { struct attach_clone_payload payload = { .ipc_socket = ipc_sockets[1], .options = options, .init_ctx = init_ctx, .exec_function = exec_function, .exec_payload = exec_payload, .procfd = procfd }; /* We use clone_parent here to make this subprocess a direct child of * the initial process. Then this intermediate process can exit and * the parent can directly track the attached process. */ pid = lxc_clone(attach_child_main, &payload, CLONE_PARENT); } /* shouldn't happen, clone() should always return positive pid */ if (pid <= 0) { SYSERROR("failed to create subprocess"); shutdown(ipc_sockets[1], SHUT_RDWR); rexit(-1); } /* tell grandparent the pid of the pid of the newly created child */ ret = lxc_write_nointr(ipc_sockets[1], &pid, sizeof(pid)); if (ret != sizeof(pid)) { /* if this really happens here, this is very unfortunate, since the * parent will not know the pid of the attached process and will * not be able to wait for it (and we won't either due to CLONE_PARENT) * so the parent won't be able to reap it and the attached process * will remain a zombie */ ERROR("error using IPC to notify main process of pid of the attached process"); shutdown(ipc_sockets[1], SHUT_RDWR); rexit(-1); } /* the rest is in the hands of the initial and the attached process */ rexit(0); }
static int attach_child_main(void* data) { struct attach_clone_payload* payload = (struct attach_clone_payload*)data; int ipc_socket = payload->ipc_socket; int procfd = payload->procfd; lxc_attach_options_t* options = payload->options; struct lxc_proc_context_info* init_ctx = payload->init_ctx; #if HAVE_SYS_PERSONALITY_H long new_personality; #endif int ret; int status; int expected; long flags; int fd; uid_t new_uid; gid_t new_gid; /* wait for the initial thread to signal us that it's ready * for us to start initializing */ expected = 0; status = -1; ret = lxc_read_nointr_expect(ipc_socket, &status, sizeof(status), &expected); if (ret <= 0) { ERROR("error using IPC to receive notification from initial process (0)"); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } /* A description of the purpose of this functionality is * provided in the lxc-attach(1) manual page. We have to * remount here and not in the parent process, otherwise * /proc may not properly reflect the new pid namespace. */ if (!(options->namespaces & CLONE_NEWNS) && (options->attach_flags & LXC_ATTACH_REMOUNT_PROC_SYS)) { ret = lxc_attach_remount_sys_proc(); if (ret < 0) { shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } } /* now perform additional attachments*/ #if HAVE_SYS_PERSONALITY_H if (options->personality < 0) new_personality = init_ctx->personality; else new_personality = options->personality; if (options->attach_flags & LXC_ATTACH_SET_PERSONALITY) { ret = personality(new_personality); if (ret < 0) { SYSERROR("could not ensure correct architecture"); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } } #endif if (options->attach_flags & LXC_ATTACH_DROP_CAPABILITIES) { ret = lxc_attach_drop_privs(init_ctx); if (ret < 0) { ERROR("could not drop privileges"); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } } /* always set the environment (specify (LXC_ATTACH_KEEP_ENV, NULL, NULL) if you want this to be a no-op) */ ret = lxc_attach_set_environment(options->env_policy, options->extra_env_vars, options->extra_keep_env); if (ret < 0) { ERROR("could not set initial environment for attached process"); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } /* set user / group id */ new_uid = 0; new_gid = 0; /* ignore errors, we will fall back to root in that case * (/proc was not mounted etc.) */ if (options->namespaces & CLONE_NEWUSER) lxc_attach_get_init_uidgid(&new_uid, &new_gid); if (options->uid != (uid_t)-1) new_uid = options->uid; if (options->gid != (gid_t)-1) new_gid = options->gid; /* setup the control tty */ if (options->stdin_fd && isatty(options->stdin_fd)) { if (setsid() < 0) { SYSERROR("unable to setsid"); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } if (ioctl(options->stdin_fd, TIOCSCTTY, (char *)NULL) < 0) { SYSERROR("unable to TIOCSTTY"); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } } /* try to set the uid/gid combination */ if ((new_gid != 0 || options->namespaces & CLONE_NEWUSER)) { if (setgid(new_gid) || setgroups(0, NULL)) { SYSERROR("switching to container gid"); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } } if ((new_uid != 0 || options->namespaces & CLONE_NEWUSER) && setuid(new_uid)) { SYSERROR("switching to container uid"); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } /* tell initial process it may now put us into the cgroups */ status = 1; ret = lxc_write_nointr(ipc_socket, &status, sizeof(status)); if (ret != sizeof(status)) { ERROR("error using IPC to notify initial process for initialization (1)"); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } /* wait for the initial thread to signal us that it has done * everything for us when it comes to cgroups etc. */ expected = 2; status = -1; ret = lxc_read_nointr_expect(ipc_socket, &status, sizeof(status), &expected); if (ret <= 0) { ERROR("error using IPC to receive final notification from initial process (2)"); shutdown(ipc_socket, SHUT_RDWR); rexit(-1); } shutdown(ipc_socket, SHUT_RDWR); close(ipc_socket); /* set new apparmor profile/selinux context */ if ((options->namespaces & CLONE_NEWNS) && (options->attach_flags & LXC_ATTACH_LSM) && init_ctx->lsm_label) { int on_exec; on_exec = options->attach_flags & LXC_ATTACH_LSM_EXEC ? 1 : 0; if (lsm_set_label_at(procfd, on_exec, init_ctx->lsm_label) < 0) { rexit(-1); } } if (init_ctx->container && init_ctx->container->lxc_conf && lxc_seccomp_load(init_ctx->container->lxc_conf) != 0) { ERROR("Loading seccomp policy"); rexit(-1); } lxc_proc_put_context_info(init_ctx); /* The following is done after the communication socket is * shut down. That way, all errors that might (though * unlikely) occur up until this point will have their messages * printed to the original stderr (if logging is so configured) * and not the fd the user supplied, if any. */ /* fd handling for stdin, stdout and stderr; * ignore errors here, user may want to make sure * the fds are closed, for example */ if (options->stdin_fd >= 0 && options->stdin_fd != 0) dup2(options->stdin_fd, 0); if (options->stdout_fd >= 0 && options->stdout_fd != 1) dup2(options->stdout_fd, 1); if (options->stderr_fd >= 0 && options->stderr_fd != 2) dup2(options->stderr_fd, 2); /* close the old fds */ if (options->stdin_fd > 2) close(options->stdin_fd); if (options->stdout_fd > 2) close(options->stdout_fd); if (options->stderr_fd > 2) close(options->stderr_fd); /* try to remove CLOEXEC flag from stdin/stdout/stderr, * but also here, ignore errors */ for (fd = 0; fd <= 2; fd++) { flags = fcntl(fd, F_GETFL); if (flags < 0) continue; if (flags & FD_CLOEXEC) { if (fcntl(fd, F_SETFL, flags & ~FD_CLOEXEC) < 0) { SYSERROR("Unable to clear CLOEXEC from fd"); } } } /* we don't need proc anymore */ close(procfd); /* we're done, so we can now do whatever the user intended us to do */ rexit(payload->exec_function(payload->exec_payload)); }