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 <xavi92@disroot.org>2025-09-24 11:03:39 +0200
commit7407ddf0162cc32fee03a309186588047a65f42a (patch)
treedb75824f0e68e2702ef78293b6dd374502e03dd8
parent7e34b2d141858eeae3ffb9e514bd55a767a52492 (diff)
downloadslcl-7407ddf0162cc32fee03a309186588047a65f42a.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 e0e881e..d56927a 100644
--- a/main.c
+++ b/main.c
@@ -7,6 +7,7 @@
#endif
#include "auth.h"
+#include "crealpath.h"
#include "cftw.h"
#include "hex.h"
#include "page.h"
@@ -39,6 +40,14 @@ struct form
char *key, *value;
};
+struct user_args
+{
+ struct auth *a;
+ struct dynstr d;
+ int fd;
+ bool fifo;
+};
+
static struct handler *handler;
static int redirect(struct http_response *const r)
@@ -60,9 +69,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);
@@ -72,8 +81,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);
@@ -301,7 +310,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)))
@@ -311,7 +320,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__);
@@ -348,9 +357,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)
{
@@ -443,8 +452,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),
*const file = p->resource + strlen("/public/");
struct dynstr d;
@@ -717,7 +726,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 *);
@@ -778,7 +788,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))
{
@@ -969,7 +980,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))
{
@@ -1051,7 +1063,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))
{
@@ -1190,6 +1203,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;
@@ -1221,10 +1321,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);
@@ -1256,10 +1358,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;
@@ -1317,8 +1433,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);
@@ -1360,7 +1477,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)
{
@@ -1377,9 +1494,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);
@@ -1394,14 +1511,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;
@@ -1605,14 +1723,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);
@@ -1676,36 +1794,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;
@@ -1716,6 +1880,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;
@@ -1726,10 +1903,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;
@@ -1754,15 +1933,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;
@@ -1772,8 +1959,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++)
{
@@ -1788,7 +1975,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;
@@ -1803,7 +1990,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;
@@ -1854,7 +2042,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__);
@@ -1891,12 +2079,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;
@@ -1905,10 +2093,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;
@@ -2185,24 +2377,67 @@ static int init_signals(void)
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 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,
.post =
{
/* Arbitrary limit. */
@@ -2215,7 +2450,7 @@ int main(int argc, char *argv[])
unsigned short outport;
if (!(handler = handler_alloc(&cfg))
- || add_urls(handler, a)
+ || add_urls(handler, &ua)
|| init_signals()
|| handler_listen(handler, port, &outport))
goto end;
@@ -2231,7 +2466,13 @@ int main(int argc, char *argv[])
ret = EXIT_SUCCESS;
end:
- auth_free(a);
handler_free(handler);
+
+ if (user_args_free(&ua))
+ {
+ fprintf(stderr, "%s: user_args_free failed\n", __func__);
+ ret = EXIT_FAILURE;
+ }
+
return ret;
}