aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXavier Del Campo Romero <xavi.dcr@tutanota.com>2023-07-24 23:25:46 +0200
committerXavier Del Campo Romero <xavi.dcr@tutanota.com>2023-11-24 01:54:12 +0100
commitb964429c67bdcba8764ce719758566700cc66bab (patch)
treeadcdcc79d0d0e13635deec8cbc5d2946da585ca2
parent18fefd8847f27e84b01edc9fe5d5a9eaae9055f0 (diff)
downloadslcl-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.c335
1 files changed, 288 insertions, 47 deletions
diff --git a/main.c b/main.c
index 3f712d6..848c94c 100644
--- a/main.c
+++ b/main.c
@@ -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;
}