Changeset View
Standalone View
main.c
/* $Id$ */ | /* $Id$ */ | ||||
/* | /* | ||||
* Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> | * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> | ||||
* | * | ||||
* Permission to use, copy, modify, and distribute this software for any | * Permission to use, copy, modify, and distribute this software for any | ||||
* purpose with or without fee is hereby granted, provided that the above | * purpose with or without fee is hereby granted, provided that the above | ||||
* copyright notice and this permission notice appear in all copies. | * copyright notice and this permission notice appear in all copies. | ||||
* | * | ||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
*/ | */ | ||||
#include <sys/event.h> | |||||
#include <sys/procdesc.h> | |||||
#include <sys/queue.h> | #include <sys/queue.h> | ||||
#include <sys/stat.h> | #include <sys/stat.h> | ||||
#include <sys/socket.h> | #include <sys/socket.h> | ||||
#include <sys/wait.h> | #include <sys/wait.h> | ||||
#include <assert.h> | #include <assert.h> | ||||
#include <err.h> | #include <err.h> | ||||
#include <getopt.h> | #include <getopt.h> | ||||
▲ Show 20 Lines • Show All 269 Lines • ▼ Show 20 Lines | fargs_parse(size_t argc, char *argv[], struct opts *opts) | ||||
return f; | return f; | ||||
} | } | ||||
int | int | ||||
main(int argc, char *argv[]) | main(int argc, char *argv[]) | ||||
{ | { | ||||
struct opts opts; | struct opts opts; | ||||
pid_t child; | pid_t child; | ||||
int fds[2], c, st; | int fds[2], c, pd, kq, nev; | ||||
struct kevent ev, rev; | |||||
struct fargs *fargs; | struct fargs *fargs; | ||||
struct option lopts[] = { | struct option lopts[] = { | ||||
{ "port", required_argument, NULL, 3 }, | { "port", required_argument, NULL, 3 }, | ||||
{ "rsh", required_argument, NULL, 'e' }, | { "rsh", required_argument, NULL, 'e' }, | ||||
{ "rsync-path", required_argument, NULL, 1 }, | { "rsync-path", required_argument, NULL, 1 }, | ||||
{ "sender", no_argument, &opts.sender, 1 }, | { "sender", no_argument, &opts.sender, 1 }, | ||||
{ "server", no_argument, &opts.server, 1 }, | { "server", no_argument, &opts.server, 1 }, | ||||
{ "dry-run", no_argument, &opts.dry_run, 1 }, | { "dry-run", no_argument, &opts.dry_run, 1 }, | ||||
Show All 18 Lines | struct option lopts[] = { | ||||
{ "specials", no_argument, &opts.specials, 1 }, | { "specials", no_argument, &opts.specials, 1 }, | ||||
{ "no-specials", no_argument, &opts.specials, 0 }, | { "no-specials", no_argument, &opts.specials, 0 }, | ||||
{ "times", no_argument, &opts.preserve_times, 1 }, | { "times", no_argument, &opts.preserve_times, 1 }, | ||||
{ "no-times", no_argument, &opts.preserve_times, 0 }, | { "no-times", no_argument, &opts.preserve_times, 0 }, | ||||
{ "verbose", no_argument, &opts.verbose, 1 }, | { "verbose", no_argument, &opts.verbose, 1 }, | ||||
{ "no-verbose", no_argument, &opts.verbose, 0 }, | { "no-verbose", no_argument, &opts.verbose, 0 }, | ||||
{ NULL, 0, NULL, 0 }}; | { NULL, 0, NULL, 0 }}; | ||||
/* Global pledge. */ | |||||
if (pledge("stdio unix rpath wpath cpath dpath inet fattr chown dns getpw proc exec unveil", | |||||
NULL) == -1) | |||||
err(EXIT_FAILURE, "pledge"); | |||||
memset(&opts, 0, sizeof(struct opts)); | memset(&opts, 0, sizeof(struct opts)); | ||||
while ((c = getopt_long(argc, argv, "Dae:ghlnoprtv", lopts, NULL)) != -1) { | while ((c = getopt_long(argc, argv, "Dae:ghlnoprtv", lopts, NULL)) != -1) { | ||||
switch (c) { | switch (c) { | ||||
case 'D': | case 'D': | ||||
opts.devices = 1; | opts.devices = 1; | ||||
opts.specials = 1; | opts.specials = 1; | ||||
break; | break; | ||||
▲ Show 20 Lines • Show All 67 Lines • ▼ Show 20 Lines | main(int argc, char *argv[]) | ||||
/* | /* | ||||
* This is what happens when we're started with the "hidden" | * This is what happens when we're started with the "hidden" | ||||
* --server option, which is invoked for the rsync on the remote | * --server option, which is invoked for the rsync on the remote | ||||
* host by the parent. | * host by the parent. | ||||
*/ | */ | ||||
if (opts.server) { | if (opts.server) { | ||||
if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw unveil", NULL) == -1) | |||||
err(EXIT_FAILURE, "pledge"); | |||||
c = rsync_server(&opts, (size_t)argc, argv); | c = rsync_server(&opts, (size_t)argc, argv); | ||||
return c ? EXIT_SUCCESS : EXIT_FAILURE; | return c ? EXIT_SUCCESS : EXIT_FAILURE; | ||||
} | } | ||||
/* | /* | ||||
* Now we know that we're the client on the local machine | * Now we know that we're the client on the local machine | ||||
* invoking rsync(1). | * invoking rsync(1). | ||||
* At this point, we need to start the client and server | * At this point, we need to start the client and server | ||||
Show All 9 Lines | main(int argc, char *argv[]) | ||||
/* | /* | ||||
* If we're contacting an rsync:// daemon, then we don't need to | * If we're contacting an rsync:// daemon, then we don't need to | ||||
* fork, because we won't start a server ourselves. | * fork, because we won't start a server ourselves. | ||||
* Route directly into the socket code, in that case. | * Route directly into the socket code, in that case. | ||||
*/ | */ | ||||
if (fargs->remote) { | if (fargs->remote) { | ||||
assert(fargs->mode == FARGS_RECEIVER); | assert(fargs->mode == FARGS_RECEIVER); | ||||
if (pledge("stdio unix rpath wpath cpath dpath inet fattr chown dns getpw unveil", | |||||
NULL) == -1) | |||||
err(EXIT_FAILURE, "pledge"); | |||||
c = rsync_socket(&opts, fargs); | c = rsync_socket(&opts, fargs); | ||||
fargs_free(fargs); | fargs_free(fargs); | ||||
return c ? EXIT_SUCCESS : EXIT_FAILURE; | return c ? EXIT_SUCCESS : EXIT_FAILURE; | ||||
} | } | ||||
/* Drop the dns/inet possibility. */ | |||||
if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw proc exec unveil", | |||||
NULL) == -1) | |||||
err(EXIT_FAILURE, "pledge"); | |||||
/* Create a bidirectional socket and start our child. */ | /* Create a bidirectional socket and start our child. */ | ||||
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, fds) == -1) | if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, fds) == -1) | ||||
err(EXIT_FAILURE, "socketpair"); | err(EXIT_FAILURE, "socketpair"); | ||||
if ((child = fork()) == -1) { | if ((child = pdfork(&pd, 0)) == -1) { | ||||
close(fds[0]); | close(fds[0]); | ||||
close(fds[1]); | close(fds[1]); | ||||
err(EXIT_FAILURE, "fork"); | err(EXIT_FAILURE, "fork"); | ||||
} | } | ||||
/* Drop the fork possibility. */ | |||||
if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw exec unveil", NULL) == -1) | |||||
err(EXIT_FAILURE, "pledge"); | |||||
if (child == 0) { | if (child == 0) { | ||||
close(fds[0]); | close(fds[0]); | ||||
fds[0] = -1; | fds[0] = -1; | ||||
if (pledge("stdio exec", NULL) == -1) | |||||
err(EXIT_FAILURE, "pledge"); | |||||
rsync_child(&opts, fds[1], fargs); | rsync_child(&opts, fds[1], fargs); | ||||
/* NOTREACHED */ | /* NOTREACHED */ | ||||
} | } | ||||
close(fds[1]); | close(fds[1]); | ||||
fds[1] = -1; | fds[1] = -1; | ||||
if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw unveil", NULL) == -1) | |||||
err(EXIT_FAILURE, "pledge"); | |||||
c = rsync_client(&opts, fds[0], fargs); | c = rsync_client(&opts, fds[0], fargs); | ||||
fargs_free(fargs); | fargs_free(fargs); | ||||
/* | /* | ||||
* If the client has an error and exits, the server may be | * If the client has an error and exits, the server may be | ||||
* sitting around waiting to get data while we waitpid(). | * sitting around waiting to get data while we waitpid(). | ||||
* So close the connection here so that they don't hang. | * So close the connection here so that they don't hang. | ||||
*/ | */ | ||||
if (!c) { | if (!c) { | ||||
close(fds[0]); | close(fds[0]); | ||||
fds[0] = -1; | fds[0] = -1; | ||||
} | } | ||||
if (waitpid(child, &st, 0) == -1) | if ((kq = kqueue()) == -1) { | ||||
err(EXIT_FAILURE, "waitpid"); | perror("kqueue"); | ||||
if (!(WIFEXITED(st) && WEXITSTATUS(st) == EXIT_SUCCESS)) | exit(EXIT_FAILURE); | ||||
} | |||||
EV_SET(&ev, pd, EVFILT_PROCDESC, EV_ADD|EV_ENABLE|EV_ONESHOT, | |||||
NOTE_EXIT, 0, 0); | |||||
nev = kevent(kq, &ev, 1, &rev, 1, NULL); | |||||
if (nev == -1) | |||||
err(EXIT_FAILURE, "kevent"); | |||||
if ((rev.fflags & NOTE_EXIT) == 0) | |||||
err(EXIT_FAILURE, "Something other than NOTE_EXIT"); | |||||
if (!(WIFEXITED(rev.data) && WEXITSTATUS(rev.data) == EXIT_SUCCESS)) | |||||
cem: I don't understand why we replaced the fork + waitpid mechanism with pdfork + kevent here (this… | |||||
borako.ozarslan_gmail.comAuthorUnsubmitted Done Inline Actionswaitpid is not allowed once you cap_enter. The only other way to waitpid + get the exit code is kevent that I found and I need the process descriptor for that. borako.ozarslan_gmail.com: waitpid is not allowed once you cap_enter. The only other way to waitpid + get the exit code is… | |||||
cemUnsubmitted Not Done Inline ActionsI'm confused where we cap_enter. Is it during rsync_client()? I'm also confused why we don't allow waitpid in capability mode. Maybe historical artifact? Obviously, we expose the same information via kevent, so either it should be allowed, or kevent shouldn't be... cem: I'm confused where we cap_enter. Is it during rsync_client()?
I'm also confused why we don't… | |||||
borako.ozarslan_gmail.comAuthorUnsubmitted Done Inline Actionsrsync_client() calls either rsync_sender() or rsync_receiver(). That's where we cap_enter. So technically yes, we're in capability mode after rsync_client. This is the only reference I found as to why waitpid isn't allowed https://lists.cam.ac.uk/pipermail/cl-capsicum-discuss/2010-November/msg00001.html borako.ozarslan_gmail.com: rsync_client() calls either rsync_sender() or rsync_receiver(). That's where we cap_enter. So… | |||||
borako.ozarslan_gmail.comAuthorUnsubmitted Done Inline ActionsThere is also supposed to be a pdwait but that's not implemented. borako.ozarslan_gmail.com: There is also supposed to be a pdwait but that's not implemented. | |||||
cemUnsubmitted Not Done Inline ActionsThis email from Robert on that thread seems to suggest waitpid should just be allowed: https://lists.cam.ac.uk/pipermail/cl-capsicum-discuss/2010-November/msg00003.html cem: This email from Robert on that thread seems to suggest waitpid should just be allowed: https… | |||||
c = 0; | c = 0; | ||||
if (fds[0] != -1) | if (fds[0] != -1) | ||||
close(fds[0]); | close(fds[0]); | ||||
return c ? EXIT_SUCCESS : EXIT_FAILURE; | return c ? EXIT_SUCCESS : EXIT_FAILURE; | ||||
usage: | usage: | ||||
fprintf(stderr, "usage: %s [-Daghlnoprtv] " | fprintf(stderr, "usage: %s [-Daghlnoprtv] " | ||||
"[-e ssh-prog] [--delete] [--numeric-ids] " | "[-e ssh-prog] [--delete] [--numeric-ids] " | ||||
"[--port=port] [--rsync-path=prog] src ... dst\n", | "[--port=port] [--rsync-path=prog] src ... dst\n", | ||||
getprogname()); | getprogname()); | ||||
return EXIT_FAILURE; | return EXIT_FAILURE; | ||||
} | } |
I don't understand why we replaced the fork + waitpid mechanism with pdfork + kevent here (this is two questions — pdfork doesn't necessitate kqueue).