Exemple #1
0
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;
}
Exemple #2
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(&current, &modEEGCfg);
    writeEDFString(&current, 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;
}
Exemple #3
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);
}
Exemple #4
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 ;
}
Exemple #6
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);
}
Exemple #7
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));
}