[hackers] [quark] Introduce flag-centric usage || Laslo Hunhold
commit 6b55e36036d52c07803824048080a1e350fe6c8c
Author: Laslo Hunhold <dev_AT_frign.de>
AuthorDate: Mon Mar 5 00:14:25 2018 +0100
Commit: Laslo Hunhold <dev_AT_frign.de>
CommitDate: Mon Mar 5 00:14:25 2018 +0100
Introduce flag-centric usage
The config.h-interface has proven to be very effective for a lot of
suckless tools, but it just does not make too much sense for a web
server like quark.
$ quark
If you run multiple instances of it, you want to see in the command line
(or top) what it does, and given the amount of options it's logical to
just express them as options given in the command line.
It also is a problem if you can modify quark via the config.h,
contradicting the manual. Just saying "Well, then don't touch config.h"
is also not good, as the vhost and map options were only exposed via
this interface.
What is left in config.h are mime-types and two constants relating to
the incoming HTTP-header-limits.
In order to introduce these changes, some structs and safe utility
functions were added and imported from OpenBSD respectively.
diff --git a/LICENSE b/LICENSE
index 90778ad..3f7cb9f 100644
--- a/LICENSE
+++ b/LICENSE
_AT_@ -4,6 +4,7 @@ Copyright 2016-2018 Laslo Hunhold <dev_AT_frign.de>
Copyright 2004 Ted Unangst <tedu_AT_openbsd.org>
Copyright 2004 Todd C. Miller <Todd.Miller_AT_courtesan.com>
+Copyright 2008 Otto Moerbeek <otto_AT_drijf.net>
Copyright 2017 Hiltjo Posthuma <hiltjo_AT_codemadness.org>
Copyright 2017 Quentin Rameau <quinq_AT_fifth.space>
Copyright 2018 Josuah Demangeon <mail_AT_josuah.net>
diff --git a/config.def.h b/config.def.h
index c7e84d8..2d3cabf 100644
--- a/config.def.h
+++ b/config.def.h
_AT_@ -1,40 +1,6 @@
-static const char *host = "localhost";
-static const char *port = "80";
-static const char *servedir = ".";
-static const char *docindex = "index.html";
-static const char *user = "nobody";
-static const char *group = "nogroup";
-
-static int listdirs = 1;
-static int vhosts = 0;
-
-static const int maxnprocs = 512;
#define HEADER_MAX 4096
#define FIELD_MAX 200
-/* virtual hosts */
-static struct {
- const char *name;
- const char *regex;
- const char *dir;
- const char *prefix;
- regex_t re;
-} vhost[] = {
- /* canonical host host regex directory prefix */
- { "example.org", "^(www\\.)?example\\.org$", "/example.org", NULL },
- { "example.org", "^old\\.example\\.org$", "/", "/old" },
-};
-
-/* target prefix mapping */
-static const struct {
- const char *vhost;
- const char *from;
- const char *to;
-} map[] = {
- /* canonical host from to */
- { "example.org", "/old/path/to/dir", "/new/path/to/dir" },
-};
-
/* mime-types */
static const struct {
char *ext;
diff --git a/http.c b/http.c
index d58d94a..0d758ad 100644
--- a/http.c
+++ b/http.c
_AT_@ -325,46 +325,47 @@ http_send_response(int fd, struct request *r)
/* match vhost */
vhostmatch = NULL;
- if (vhosts) {
- for (i = 0; i < LEN(vhost); i++) {
+ if (s.vhost) {
+ for (i = 0; i < s.vhost_len; i++) {
/* switch to vhost directory if there is a match */
- if (!regexec(&vhost[i].re, r->field[REQ_HOST], 0,
+ if (!regexec(&s.vhost[i].re, r->field[REQ_HOST], 0,
NULL, 0)) {
- if (chdir(vhost[i].dir) < 0) {
+ if (chdir(s.vhost[i].dir) < 0) {
return http_send_status(fd, (errno == EACCES) ?
S_FORBIDDEN : S_NOT_FOUND);
}
- vhostmatch = vhost[i].name;
+ vhostmatch = s.vhost[i].name;
break;
}
}
- if (i == LEN(vhost)) {
+ if (i == s.vhost_len) {
return http_send_status(fd, S_NOT_FOUND);
}
/* if we have a vhost prefix, prepend it to the target */
- if (vhost[i].prefix) {
+ if (s.vhost[i].prefix) {
if (snprintf(realtarget, sizeof(realtarget), "%s%s",
- vhost[i].prefix, realtarget) >= sizeof(realtarget)) {
+ s.vhost[i].prefix, realtarget) >= sizeof(realtarget)) {
return http_send_status(fd, S_REQUEST_TOO_LARGE);
}
}
}
/* apply target prefix mapping */
- for (i = 0; i < LEN(map); i++) {
- len = strlen(map[i].from);
- if (!strncmp(realtarget, map[i].from, len)) {
+ for (i = 0; i < s.map_len; i++) {
+ len = strlen(s.map[i].from);
+ if (!strncmp(realtarget, s.map[i].from, len)) {
/* match canonical host if vhosts are enabled */
- if (vhosts && strcmp(map[i].vhost, vhostmatch)) {
+ if (s.vhost && strcmp(s.map[i].chost, vhostmatch)) {
continue;
}
/* swap out target prefix */
- if (snprintf(realtarget, sizeof(realtarget), "%s%s", map[i].to,
- realtarget + len) >= sizeof(realtarget)) {
+ if (snprintf(tmptarget, sizeof(tmptarget), "%s%s", s.map[i].to,
+ realtarget + len) >= sizeof(tmptarget)) {
return http_send_status(fd, S_REQUEST_TOO_LARGE);
}
+ memcpy(realtarget, tmptarget, sizeof(realtarget));
break;
}
}
_AT_@ -398,16 +399,17 @@ http_send_response(int fd, struct request *r)
}
/* redirect if targets differ, host is non-canonical or we prefixed */
- if (strcmp(r->target, realtarget) || (vhosts && vhostmatch &&
+ if (strcmp(r->target, realtarget) || (s.vhost && vhostmatch &&
strcmp(r->field[REQ_HOST], vhostmatch))) {
/* do we need to add a port to the Location? */
- hasport = strcmp(port, "80");
+ hasport = s.port && strcmp(s.port, "80");
/* RFC 2732 specifies to use brackets for IPv6-addresses in
* URLs, so we need to check if our host is one and honor that
* later when we fill the "Location"-field */
if ((ipv6host = inet_pton(AF_INET6, r->field[REQ_HOST][0] ?
- r->field[REQ_HOST] : host, &res)) < 0) {
+ r->field[REQ_HOST] : s.host ? s.host :
+ "localhost", &res)) < 0) {
return http_send_status(fd, S_INTERNAL_SERVER_ERROR);
}
_AT_@ -424,10 +426,11 @@ http_send_response(int fd, struct request *r)
S_MOVED_PERMANENTLY,
status_str[S_MOVED_PERMANENTLY],
timestamp(time(NULL), t), ipv6host ? "[" : "",
- r->field[REQ_HOST][0] ? (vhosts && vhostmatch) ?
- vhostmatch : r->field[REQ_HOST] : host,
+ r->field[REQ_HOST][0] ? (s.vhost && vhostmatch) ?
+ vhostmatch : r->field[REQ_HOST] : s.host ?
+ s.host : "localhost",
ipv6host ? "]" : "", hasport ? ":" : "",
- hasport ? port : "", tmptarget) < 0) {
+ hasport ? s.port : "", tmptarget) < 0) {
return S_REQUEST_TIMEOUT;
}
_AT_@ -437,16 +440,16 @@ http_send_response(int fd, struct request *r)
if (S_ISDIR(st.st_mode)) {
/* append docindex to target */
if (snprintf(realtarget, sizeof(realtarget), "%s%s",
- r->target, docindex) >= sizeof(realtarget)) {
+ r->target, s.docindex) >= sizeof(realtarget)) {
return http_send_status(fd, S_REQUEST_TOO_LARGE);
}
/* stat the docindex, which must be a regular file */
if (stat(RELPATH(realtarget), &st) < 0 || !S_ISREG(st.st_mode)) {
- if (listdirs) {
+ if (s.listdirs) {
/* remove index suffix and serve dir */
realtarget[strlen(realtarget) -
- strlen(docindex)] = '\0';
+ strlen(s.docindex)] = '\0';
return resp_dir(fd, RELPATH(realtarget), r);
} else {
/* reject */
diff --git a/main.c b/main.c
index 28b5163..7bcaeed 100644
--- a/main.c
+++ b/main.c
_AT_@ -1,6 +1,7 @@
/* See LICENSE file for copyright and license details. */
#include <errno.h>
#include <grp.h>
+#include <limits.h>
#include <netinet/in.h>
#include <pwd.h>
#include <regex.h>
_AT_@ -19,8 +20,6 @@
#include "sock.h"
#include "util.h"
-#include "config.h"
-
static char *udsname;
static void
_AT_@ -94,8 +93,12 @@ handlesignals(void(*hdl)(int))
static void
usage(void)
{
- die("usage: %s [-l | -L] [-v | -V] [[[-h host] [-p port]] | [-U sockfile]] "
- "[-d dir] [-u user] [-g group]", argv0);
+ const char *opts = "[-u user] [-g group] [-n num] [-d dir] [-l] "
+ "[-i index] [-v vhost] ... [-m map] ...";
+
+ die("usage: %s -h host -p port %s\n"
+ " %s -U socket [-p port] %s", argv0,
+ opts, argv0, opts);
}
int
_AT_@ -108,37 +111,86 @@ main(int argc, char *argv[])
pid_t cpid, wpid, spid;
socklen_t in_sa_len;
int i, insock, status = 0, infd;
+ const char *err;
+ char *tok;
+
+ /* defaults */
+ int maxnprocs = 512;
+ char *servedir = ".";
+ char *user = "nobody";
+ char *group = "nogroup";
+
+ s.host = s.port = NULL;
+ s.vhost = NULL;
+ s.map = NULL;
+ s.vhost_len = s.map_len = 0;
+ s.docindex = "index.html";
+ s.listdirs = 0;
ARGBEGIN {
- case 'd':
- servedir = EARGF(usage());
- break;
- case 'g':
- group = EARGF(usage());
- break;
case 'h':
- host = EARGF(usage());
- break;
- case 'l':
- listdirs = 0;
- break;
- case 'L':
- listdirs = 1;
+ s.host = EARGF(usage());
break;
case 'p':
- port = EARGF(usage());
+ s.port = EARGF(usage());
+ break;
+ case 'U':
+ udsname = EARGF(usage());
break;
case 'u':
user = EARGF(usage());
break;
- case 'U':
- udsname = EARGF(usage());
+ case 'g':
+ group = EARGF(usage());
+ break;
+ case 'n':
+ maxnprocs = strtonum(EARGF(usage()), 1, INT_MAX, &err);
+ if (err) {
+ die("strtonum '%s': %s", EARGF(usage()), err);
+ }
+ break;
+ case 'd':
+ servedir = EARGF(usage());
+ break;
+ case 'l':
+ s.listdirs = 1;
+ break;
+ case 'i':
+ s.docindex = EARGF(usage());
+ if (strchr(s.docindex, '/')) {
+ die("The document index must not contain '/'");
+ }
break;
case 'v':
- vhosts = 0;
+ if (!(tok = strdup(EARGF(usage())))) {
+ die("strdup:");
+ }
+ if (!(s.vhost = reallocarray(s.vhost, s.vhost_len++,
+ sizeof(struct vhost)))) {
+ die("reallocarray:");
+ }
+ if (!(s.vhost[s.vhost_len - 1].name = strtok(tok, " ")) ||
+ !(s.vhost[s.vhost_len - 1].regex = strtok(NULL, " ")) ||
+ !(s.vhost[s.vhost_len - 1].dir = strtok(NULL, " ")) ||
+ !(s.vhost[s.vhost_len - 1].prefix = strtok(NULL, " ")) ||
+ strtok(NULL, "")) {
+ usage();
+ }
break;
- case 'V':
- vhosts = 1;
+ case 'm':
+ if (!(tok = strdup(EARGF(usage())))) {
+ die("strdup:");
+ }
+ if (!(s.map = reallocarray(s.map, s.map_len++,
+ sizeof(struct map)))) {
+ die("reallocarray:");
+ }
+ if (!(s.map[s.map_len - 1].chost = strtok(tok, " ")) ||
+ !(s.map[s.map_len - 1].from = strtok(NULL, " ")) ||
+ !(s.map[s.map_len - 1].to = strtok(NULL, " ")) ||
+ strtok(NULL, "")) {
+ usage();
+ }
break;
default:
usage();
_AT_@ -148,19 +200,22 @@ main(int argc, char *argv[])
usage();
}
+ /* allow either host or UNIX-domain socket, force port with host */
+ if ((s.host && udsname) || (s.host && !s.port)) {
+ usage();
+ }
+
if (udsname && (!access(udsname, F_OK) || errno != ENOENT)) {
die("UNIX-domain socket: %s", errno ?
strerror(errno) : "file exists");
}
/* compile and check the supplied vhost regexes */
- if (vhosts) {
- for (i = 0; i < LEN(vhost); i++) {
- if (regcomp(&vhost[i].re, vhost[i].regex,
- REG_EXTENDED | REG_ICASE | REG_NOSUB)) {
- die("regcomp '%s': invalid regex",
- vhost[i].regex);
- }
+ for (i = 0; i < s.vhost_len; i++) {
+ if (regcomp(&s.vhost[i].re, s.vhost[i].regex,
+ REG_EXTENDED | REG_ICASE | REG_NOSUB)) {
+ die("regcomp '%s': invalid regex",
+ s.vhost[i].regex);
}
}
_AT_@ -186,7 +241,7 @@ main(int argc, char *argv[])
/* bind socket */
insock = udsname ? sock_get_uds(udsname, pwd->pw_uid, grp->gr_gid) :
- sock_get_ips(host, port);
+ sock_get_ips(s.host, s.port);
switch (cpid = fork()) {
case -1:
diff --git a/util.c b/util.c
index 1529767..1fe9db8 100644
--- a/util.c
+++ b/util.c
_AT_@ -2,14 +2,17 @@
#include <errno.h>
#include <limits.h>
#include <stdarg.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/types.h>
#include <time.h>
#include "util.h"
char *argv0;
+struct server s = { 0 };
static void
verr(const char *fmt, va_list ap)
_AT_@ -50,6 +53,14 @@ die(const char *fmt, ...)
exit(1);
}
+char *
+timestamp(time_t t, char buf[TIMESTAMP_LEN])
+{
+ strftime(buf, TIMESTAMP_LEN, "%a, %d %b %Y %T GMT", gmtime(&t));
+
+ return buf;
+}
+
#define INVALID 1
#define TOOSMALL 2
#define TOOLARGE 3
_AT_@ -93,10 +104,19 @@ strtonum(const char *numstr, long long minval, long long maxval,
return ll;
}
-char *
-timestamp(time_t t, char buf[TIMESTAMP_LEN])
-{
- strftime(buf, TIMESTAMP_LEN, "%a, %d %b %Y %T GMT", gmtime(&t));
+/*
+ * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX
+ * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW
+ */
+#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4))
- return buf;
+void *
+reallocarray(void *optr, size_t nmemb, size_t size)
+{
+ if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
+ nmemb > 0 && SIZE_MAX / nmemb < size) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ return realloc(optr, size * nmemb);
}
diff --git a/util.h b/util.h
index 7aa8584..1f9e107 100644
--- a/util.h
+++ b/util.h
_AT_@ -2,10 +2,38 @@
#ifndef UTIL_H
#define UTIL_H
+#include <regex.h>
+#include <stddef.h>
#include <time.h>
#include "arg.h"
+/* main server struct */
+struct vhost {
+ char *name;
+ char *regex;
+ char *dir;
+ char *prefix;
+ regex_t re;
+};
+
+struct map {
+ char *chost;
+ char *from;
+ char *to;
+};
+
+extern struct server {
+ char *host;
+ char *port;
+ char *docindex;
+ int listdirs;
+ struct vhost *vhost;
+ size_t vhost_len;
+ struct map *map;
+ size_t map_len;
+} s;
+
#undef MIN
#define MIN(x,y) ((x) < (y) ? (x) : (y))
#undef MAX
_AT_@ -18,10 +46,11 @@ extern char *argv0;
void warn(const char *, ...);
void die(const char *, ...);
-long long strtonum(const char *, long long, long long, const char **);
-
#define TIMESTAMP_LEN 30
char *timestamp(time_t, char buf[TIMESTAMP_LEN]);
+void *reallocarray(void *, size_t, size_t);
+long long strtonum(const char *, long long, long long, const char **);
+
#endif /* UTIL_H */
Received on Mon Mar 05 2018 - 00:31:59 CET
This archive was generated by hypermail 2.3.0
: Mon Mar 05 2018 - 00:36:26 CET