diff options
| author | Xavier Del Campo Romero <xavi.dcr@tutanota.com> | 2023-07-24 23:25:46 +0200 |
|---|---|---|
| committer | Xavier Del Campo Romero <xavi.dcr@tutanota.com> | 2023-11-24 01:54:12 +0100 |
| commit | b964429c67bdcba8764ce719758566700cc66bab (patch) | |
| tree | adcdcc79d0d0e13635deec8cbc5d2946da585ca2 | |
| parent | 18fefd8847f27e84b01edc9fe5d5a9eaae9055f0 (diff) | |
| download | slcl-b964429c67bdcba8764ce719758566700cc66bab.tar.gz | |
main.c: Add -m command line option to open a named pipe
This write-only named pipe is meant to inform other processes about
files that have been added/removed to/from the user/ directory.
The syntax is line-oriented and is described below.
For added files:
+<space ...><path><LF>
For removed files:
-<space ...><path><LF>
Paths shall always be absolute.
Examples:
+ /home/test/db/user/alice/a picture.jpg
- /home/test/db/user/bob/essay.txt
As reported above, this feature has been made completely optional so as
not to introduce breaking changes. A new command line option, namely -m,
is required to enable this feature.
| -rw-r--r-- | main.c | 335 |
1 files changed, 288 insertions, 47 deletions
@@ -1,6 +1,7 @@ #define _POSIX_C_SOURCE 200809L #include "auth.h" +#include "crealpath.h" #include "cftw.h" #include "hex.h" #include "page.h" @@ -32,6 +33,14 @@ struct form char *key, *value; }; +struct user_args +{ + struct auth *a; + struct dynstr d; + int fd; + bool fifo; +}; + static int redirect(struct http_response *const r) { *r = (const struct http_response) @@ -51,9 +60,9 @@ static int redirect(struct http_response *const r) static int serve_index(const struct http_payload *const p, struct http_response *const r, void *const user) { - struct auth *const a = user; + const struct user_args *const ua = user; - if (auth_cookie(a, &p->cookie)) + if (auth_cookie(ua->a, &p->cookie)) return page_login(r); return redirect(r); @@ -63,8 +72,8 @@ static int serve_style(const struct http_payload *const p, struct http_response *const r, void *const user) { int ret = -1; - struct auth *const a = user; - const char *const dir = auth_dir(a); + const struct user_args *const ua = user; + const char *const dir = auth_dir(ua->a); struct dynstr d; dynstr_init(&d); @@ -292,7 +301,7 @@ static int login(const struct http_payload *const pl, int ret = -1; size_t n = 0; struct form *forms = NULL; - struct auth *const a = user; + const struct user_args *const ua = user; char *cookie = NULL; if ((ret = get_forms(pl, &forms, &n))) @@ -302,7 +311,7 @@ static int login(const struct http_payload *const pl, goto end; } - else if ((ret = check_credentials(a, forms, n, &cookie))) + else if ((ret = check_credentials(ua->a, forms, n, &cookie))) { if (ret < 0) fprintf(stderr, "%s: check_credentials failed\n", __func__); @@ -339,9 +348,9 @@ end: static int logout(const struct http_payload *const p, struct http_response *const r, void *const user) { - struct auth *const a = user; + const struct user_args *const ua = user; const struct http_cookie *const c = &p->cookie; - const int res = auth_cookie(a, c); + const int res = auth_cookie(ua->a, c); if (res < 0) { @@ -424,8 +433,8 @@ static int getpublic(const struct http_payload *const p, struct http_response *const r, void *const user) { int ret = -1; - struct auth *const a = user; - const char *const adir = auth_dir(a); + const struct user_args *const ua = user; + const char *const adir = auth_dir(ua->a); struct dynstr d; dynstr_init(&d); @@ -698,7 +707,8 @@ static int search(const struct http_payload *const p, struct http_response *const r, void *const user) { int ret = -1; - const struct auth *const a = user; + const struct user_args *const args = user; + const struct auth *const a = args->a; const char *const username = p->cookie.field, *const root = auth_dir(a); struct page_search s = {0}; int (*f)(struct http_response *); @@ -759,7 +769,8 @@ end: static int share(const struct http_payload *const p, struct http_response *const r, void *const user) { - struct auth *const a = user; + const struct user_args *const ua = user; + const struct auth *const a = ua->a; if (auth_cookie(a, &p->cookie)) { @@ -916,7 +927,8 @@ static int check_length(const unsigned long long len, static int getnode(const struct http_payload *const p, struct http_response *const r, void *const user) { - struct auth *const a = user; + const struct user_args *const ua = user; + const struct auth *const a = ua->a; if (auth_cookie(a, &p->cookie)) { @@ -998,7 +1010,8 @@ end: static int getnode_head(const struct http_payload *const p, struct http_response *const r, void *const user) { - struct auth *const a = user; + const struct user_args *const args = user; + const struct auth *const a = args->a; if (auth_cookie(a, &p->cookie)) { @@ -1131,6 +1144,93 @@ static int rename_or_move(const char *const old, const char *const new) return res; } +static int fifo_writeall(int *const fd, const char *const path, + const void *buf, size_t n) +{ + if (*fd < 0 && (*fd = open(path, O_WRONLY | O_NONBLOCK)) < 0) + { + if (errno == ENXIO) + return 0; + else + { + fprintf(stderr, "%s: open(2): %s\n", __func__, strerror(errno)); + return -1; + } + } + + int ret = -1; + + while (n) + { + ssize_t res; + +again: + + if ((res = write(*fd, buf, n)) < 0) + { + switch (errno) + { + case EAGAIN: + /* Fall through. */ + case EINTR: + goto again; + + case EPIPE: + { + ret = 0; + + if (close(*fd)) + { + fprintf(stderr, "%s: close(2): %s\n", + __func__, strerror(errno)); + ret = -1; + } + else + *fd = -1; + + goto end; + } + + default: + fprintf(stderr, "%s: write(2): %s\n", + __func__, strerror(errno)); + goto end; + } + } + + n -= res; + } + + ret = 0; + +end: + return ret; +} + +static int fifo_notify_add(int *const fd, const char *const path, + const char *const body) +{ + static const char header[] = "+ ", lf[] = "\n"; + + if (fifo_writeall(fd, path, header, strlen(header))) + { + fprintf(stderr, "%s: fifo_writeall header failed\n", __func__); + return -1; + } + else if (fifo_writeall(fd, path, body, strlen(body))) + { + fprintf(stderr, "%s: fifo_writeall body failed\n", __func__); + return -1; + } + else if (fifo_writeall(fd, path, lf, strlen(lf))) + { + fprintf(stderr, "%s: fifo_writeall lf failed\n", __func__); + return -1; + } + + return 0; +} + static int check_upload_dir(const char *const dir) { struct stat sb; @@ -1162,10 +1262,12 @@ static int check_upload_dir(const char *const dir) } static int upload_file(const struct http_post_file *const f, - const char *const user, const char *const root, const char *const dir) + const char *const user, const char *const root, const char *const dir, + struct user_args *const ua) { int ret = -1; struct dynstr dird, d; + char *abspath = NULL; dynstr_init(&dird); dynstr_init(&d); @@ -1197,10 +1299,24 @@ static int upload_file(const struct http_post_file *const f, fprintf(stderr, "%s: rename_or_move failed\n", __func__); goto end; } + else if (ua->fifo) + { + if (!(abspath = crealpath(d.str))) + { + fprintf(stderr, "%s: crealpath failed\n", __func__); + goto end; + } + else if (fifo_notify_add(&ua->fd, ua->d.str, abspath)) + { + fprintf(stderr, "%s: fifo_notify_add failed\n", __func__); + goto end; + } + } ret = 0; end: + free(abspath); dynstr_free(&dird); dynstr_free(&d); return ret; @@ -1258,8 +1374,9 @@ static const char *get_upload_dir(const struct http_post *const po) } static int upload_files(const struct http_payload *const p, - struct http_response *const r, const struct auth *const a) + struct http_response *const r, struct user_args *const ua) { + const struct auth *const a = ua->a; const struct http_post *const po = &p->u.post; const char *const root = auth_dir(a), *const user = p->cookie.field, *const dir = get_upload_dir(po); @@ -1301,7 +1418,7 @@ static int upload_files(const struct http_payload *const p, for (size_t i = 0; i < po->nfiles; i++) { - const int ret = upload_file(&po->files[i], user, root, dir); + const int ret = upload_file(&po->files[i], user, root, dir, ua); if (ret < 0) { @@ -1318,9 +1435,9 @@ static int upload_files(const struct http_payload *const p, static int upload(const struct http_payload *const p, struct http_response *const r, void *const user) { - const struct auth *const a = user; + struct user_args *const ua = user; - if (auth_cookie(a, &p->cookie)) + if (auth_cookie(ua->a, &p->cookie)) { fprintf(stderr, "%s: auth_cookie failed\n", __func__); return page_forbidden(r); @@ -1335,14 +1452,15 @@ static int upload(const struct http_payload *const p, return 0; } - return upload_files(p, r, a); + return upload_files(p, r, ua); } static int createdir(const struct http_payload *const p, struct http_response *const r, void *const user) { int ret = -1; - struct auth *const a = user; + const struct user_args *const ua = user; + const struct auth *const a = ua->a; struct dynstr d, userd; struct form *forms = NULL; size_t n = 0; @@ -1539,14 +1657,14 @@ static int confirm_rm(const struct http_payload *const p, struct http_response *const r, void *const user) { int ret = -1; - struct auth *const a = user; + const struct user_args *const ua = user; struct form *forms = NULL; const char **items = NULL; size_t n = 0; int (*f)(struct http_response *); struct page_rm rm = {0}; - if (auth_cookie(a, &p->cookie)) + if (auth_cookie(ua->a, &p->cookie)) { fprintf(stderr, "%s: auth_cookie failed\n", __func__); ret = page_forbidden(r); @@ -1610,36 +1728,82 @@ static const char *find_rm_dir(const struct form *const forms, const size_t n, return dir; } +static int fifo_notify_remove(int *const fd, const char *const path, + const char *const body) +{ + static const char header[] = "- ", lf[] = "\n"; + + if (fifo_writeall(fd, path, header, strlen(header))) + { + fprintf(stderr, "%s: fifo_writeall header failed\n", __func__); + return -1; + } + else if (fifo_writeall(fd, path, body, strlen(body))) + { + fprintf(stderr, "%s: fifo_writeall body failed\n", __func__); + return -1; + } + else if (fifo_writeall(fd, path, lf, strlen(lf))) + { + fprintf(stderr, "%s: fifo_writeall lf failed\n", __func__); + return -1; + } + + return 0; +} + static int rm_dir_contents(const char *const fpath, const struct stat *const sb, bool *const done, void *const user) { + int ret = -1; + struct user_args *const ua = user; + char *abspath = NULL; + if (S_ISDIR(sb->st_mode) && rmdir(fpath)) { fprintf(stderr, "%s: rmdir(2) %s: %s\n", __func__, fpath, strerror(errno)); - return -1; + goto end; } else if (S_ISREG(sb->st_mode) && remove(fpath)) { fprintf(stderr, "%s: remove(3) %s: %s\n", __func__, fpath, strerror(errno)); - return -1; + goto end; + } + else if (ua->fifo) + { + if (!(abspath = crealpath(fpath))) + { + fprintf(stderr, "%s: crealpath failed\n", __func__); + goto end; + } + else if (fifo_notify_remove(&ua->fd, ua->d.str, abspath)) + { + fprintf(stderr, "%s: fifo_notify_remove failed\n", __func__); + goto end; + } } - return 0; + ret = 0; + +end: + free(abspath); + return ret; } -static int rmdir_r(const char *const path) +static int rmdir_r(struct user_args *const ua, const char *const path) { int ret = -1; DIR *const d = opendir(path); + char *abspath = NULL; if (!d) { fprintf(stderr, "%s: opendir(3): %s\n", __func__, strerror(errno)); goto end; } - else if (cftw(path, rm_dir_contents, NULL)) + else if (cftw(path, rm_dir_contents, ua)) { fprintf(stderr, "%s: rm_dir_contents failed\n", __func__); goto end; @@ -1650,6 +1814,19 @@ static int rmdir_r(const char *const path) __func__, path, strerror(errno)); goto end; } + else if (ua->fifo) + { + if (!(abspath = crealpath(path))) + { + fprintf(stderr, "%s: crealpath failed\n", __func__); + goto end; + } + else if (fifo_notify_remove(&ua->fd, ua->d.str, abspath)) + { + fprintf(stderr, "%s: fifo_notify_remove failed\n", __func__); + goto end; + } + } ret = 0; @@ -1660,10 +1837,12 @@ end: ret = -1; } + free(abspath); return ret; } -static int rm_item(const char *const root, const char *const item) +static int rm_item(struct user_args *const ua, const char *const root, + const char *const item) { int ret = -1; struct stat sb; @@ -1688,15 +1867,23 @@ static int rm_item(const char *const root, const char *const item) goto end; } - else if (S_ISDIR(sb.st_mode) && rmdir_r(d.str)) + else if (S_ISDIR(sb.st_mode) && rmdir_r(ua, d.str)) { fprintf(stderr, "%s: rmdir_r failed\n", __func__); goto end; } - else if (S_ISREG(sb.st_mode) && remove(d.str)) + else if (S_ISREG(sb.st_mode)) { - fprintf(stderr, "%s: remove(3): %s\n", __func__, strerror(errno)); - goto end; + if (remove(d.str)) + { + fprintf(stderr, "%s: remove(3): %s\n", __func__, strerror(errno)); + goto end; + } + else if (ua->fifo && fifo_notify_remove(&ua->fd, ua->d.str, d.str)) + { + fprintf(stderr, "%s: fifo_notify_remove failed\n", __func__); + goto end; + } } ret = 0; @@ -1706,8 +1893,8 @@ end: return ret; } -static int do_rm(const struct form *const forms, const size_t n, - const char *const dir) +static int do_rm(struct user_args *const ua, const struct form *const forms, + const size_t n, const char *const dir) { for (size_t i = 0; i < n; i++) { @@ -1722,7 +1909,7 @@ static int do_rm(const struct form *const forms, const size_t n, fprintf(stderr, "%s: invalid path %s\n", __func__, path); return 1; } - else if (rm_item(dir, f->value)) + else if (rm_item(ua, dir, f->value)) { fprintf(stderr, "%s: rm_item failed\n", __func__); return -1; @@ -1737,7 +1924,8 @@ static int rm(const struct http_payload *const p, struct http_response *const r, void *const user) { int ret = -1; - struct auth *const a = user; + struct user_args *const ua = user; + const struct auth *const a = ua->a; struct form *forms = NULL; size_t n = 0; const struct http_cookie *const c = &p->cookie; @@ -1788,7 +1976,7 @@ static int rm(const struct http_payload *const p, fprintf(stderr, "%s: dynstr_append failed\n", __func__); goto end; } - else if ((ret = do_rm(forms, n, userdir.str))) + else if ((ret = do_rm(ua, forms, n, userdir.str))) { if (ret < 0) fprintf(stderr, "%s: do_rm failed\n", __func__); @@ -1825,12 +2013,12 @@ end: static void usage(char *const argv[]) { - fprintf(stderr, "%s [-t tmpdir] [-p port] dir\n", *argv); + fprintf(stderr, "%s [-F] [-t tmpdir] [-p port] dir\n", *argv); } static int parse_args(const int argc, char *const argv[], const char **const dir, unsigned short *const port, - const char **const tmpdir) + const char **const tmpdir, bool *const fifo) { const char *const envtmp = getenv("TMPDIR"); int opt; @@ -1839,10 +2027,14 @@ static int parse_args(const int argc, char *const argv[], *port = 0; *tmpdir = envtmp ? envtmp : "/tmp"; - while ((opt = getopt(argc, argv, "t:p:")) != -1) + while ((opt = getopt(argc, argv, "Ft:p:")) != -1) { switch (opt) { + case 'F': + *fifo = true; + break; + case 't': *tmpdir = optarg; break; @@ -2068,31 +2260,74 @@ static int add_urls(struct handler *const h, void *const user) return 0; } +static int ensure_fifo(const char *const dir, struct user_args *const ua) +{ + int ret = -1; + + if (dynstr_append(&ua->d, "%s/slcl.fifo", dir)) + { + fprintf(stderr, "%s: dynstr_append failed\n", __func__); + goto end; + } + else if (mkfifo(ua->d.str, S_IRUSR | S_IWUSR)) + { + fprintf(stderr, "%s: mkfifo(3): %s\n", __func__, strerror(errno)); + goto end; + } + + ret = 0; + +end: + return ret; +} + +static int user_args_free(struct user_args *const ua) +{ + int ret = 0; + + if (ua) + { + if (ua->d.str && remove(ua->d.str)) + { + fprintf(stderr, "%s: remove(3): %s\n", __func__, strerror(errno)); + ret = -1; + } + + auth_free(ua->a); + dynstr_free(&ua->d); + } + + return ret; +} + int main(int argc, char *argv[]) { int ret = EXIT_FAILURE; struct handler *h = NULL; - struct auth *a = NULL; const char *dir, *tmpdir; unsigned short port; + struct user_args ua = {.fd = -1}; - if (parse_args(argc, argv, &dir, &port, &tmpdir) + dynstr_init(&ua.d); + + if (parse_args(argc, argv, &dir, &port, &tmpdir, &ua.fifo) || init_dirs(dir) || ensure_style(dir) - || !(a = auth_alloc(dir))) + || !(ua.a = auth_alloc(dir)) + || (ua.fifo && ensure_fifo(dir, &ua))) goto end; const struct handler_cfg cfg = { .length = check_length, .tmpdir = tmpdir, - .user = a + .user = ua.a }; unsigned short outport; if (!(h = handler_alloc(&cfg)) - || add_urls(h, a) + || add_urls(h, &ua) || handler_listen(h, port, &outport)) goto end; @@ -2107,7 +2342,13 @@ int main(int argc, char *argv[]) ret = EXIT_SUCCESS; end: - auth_free(a); handler_free(h); + + if (user_args_free(&ua)) + { + fprintf(stderr, "%s: user_args_free failed\n", __func__); + ret = EXIT_FAILURE; + } + return ret; } |
