From: Martin Mares Date: Tue, 9 Sep 2008 10:53:03 +0000 (+0200) Subject: Moved some utils from utils/ to ucw/ X-Git-Tag: holmes-import~324 X-Git-Url: http://mj.ucw.cz/gitweb/?a=commitdiff_plain;h=a9a21e8dbd6782e71283171be9ddc0d908c69626;p=libucw.git Moved some utils from utils/ to ucw/ Some of the utilities do not need sherlock (and are not anyhow related to it), so they were moved to libucw: * b224 (base224 encoder/decoder) * daemon-helper (replacement for start-stop-daemon) * hex (hex dumper) * rotate-log (log rotator) * urltool (url manipulating tool) Introduced a CONFIG_UCW_UTILS config switch which controls compilation of these utilities. Originally by Michal, with minor changes by MJ: * basecode.test is added to TESTS only if CONFIG_UCW_UTILS is set. * Changed the order of Makefile sections and Set statements to a more logical one. --- diff --git a/free/libs/default.cfg b/free/libs/default.cfg index 6a1bdc21..eb60cd3e 100644 --- a/free/libs/default.cfg +++ b/free/libs/default.cfg @@ -14,6 +14,7 @@ Set("CONFIG_UCW_THREADS" => 1); Set("CONFIG_UCW_PERL" => 1); Set("CONFIG_UCW_PERL_MODULES" => 1); Set("CONFIG_UCW_SHELL_UTILS" => 1); +Set("CONFIG_UCW_UTILS" => 1); # Libsh settings Set("CONFIG_BUCKET_SHIFT" => 6); diff --git a/ucw/Makefile b/ucw/Makefile index 5d2ce0c2..0f2dc39e 100644 --- a/ucw/Makefile +++ b/ucw/Makefile @@ -3,7 +3,11 @@ DIRS+=ucw LIBUCW=$(o)/ucw/libucw.pc -PROGS+=$(o)/ucw/basecode +ifdef CONFIG_UCW_UTILS +UCW_UTILS=basecode b224 rotate-log urltool daemon-helper hex + +PROGS+=$(addprefix $(o)/ucw/,$(UCW_UTILS)) +endif LIBUCW_MODS= \ threads \ @@ -94,7 +98,7 @@ $(o)/ucw/basecode: $(o)/ucw/basecode.o $(LIBUCW) TESTS+=$(addprefix $(o)/ucw/,regex.test unicode.test hash-test.test mempool.test stkstring.test \ slists.test kmp-test.test bbuf.test getopt.test ff-unicode.test eltpool.test \ fb-socket.test trie-test.test string.test sha1.test asort-test.test binheap-test.test \ - redblack-test.test basecode.test fb-file.test fb-grow.test fb-pool.test fb-atomic.test \ + redblack-test.test fb-file.test fb-grow.test fb-pool.test fb-atomic.test \ fb-limfd.test fb-temp.test fb-mem.test fb-buffer.test fb-mmap.test url.test) $(o)/ucw/regex.test: $(o)/ucw/regex-t @@ -115,7 +119,6 @@ $(o)/ucw/trie-test.test: $(o)/ucw/trie-test $(o)/ucw/asort-test.test: $(o)/ucw/asort-test $(o)/ucw/binheap-test.test: $(o)/ucw/binheap-test $(o)/ucw/redblack-test.test: $(o)/ucw/redblack-test -$(o)/ucw/basecode.test: $(o)/ucw/basecode $(addprefix $(o)/ucw/fb-,file.test grow.test pool.test socket.test atomic.test \ limfd.test temp.test mem.test buffer.test mmap.test): %.test: %-t $(o)/ucw/fb-atomic-tt.o: CFLAGS += -DFB_ATOMIC_TRACE @@ -141,3 +144,13 @@ endif ifdef CONFIG_UCW_SHELL_UTILS include $(s)/ucw/shell/Makefile endif + +ifdef CONFIG_UCW_UTILS +$(o)/ucw/b224: $(o)/ucw/b224.o $(LIBUCW) +$(o)/ucw/daemon-helper: $(o)/ucw/daemon-helper.o $(LIBUCW) +$(o)/ucw/urltool: $(o)/ucw/urltool.o $(LIBUCW) +$(o)/ucw/hex: $(o)/ucw/hex.o $(LIBUCW) + +TESTS+=$(o)/ucw/basecode.test +$(o)/ucw/basecode.test: $(o)/ucw/basecode +endif diff --git a/ucw/b224.c b/ucw/b224.c new file mode 100644 index 00000000..501bd143 --- /dev/null +++ b/ucw/b224.c @@ -0,0 +1,66 @@ +/* + * A Program For Manipulation With Base224 Encoded Files + * + * (c) 2002 Martin Mares + */ + +#include "ucw/lib.h" +#include "ucw/fastbuf.h" +#include "ucw/base224.h" + +#include + +int main(int argc, char **argv) +{ + struct fastbuf *in = bfdopen_shared(0, 4096); + struct fastbuf *out = bfdopen_shared(1, 4096); + byte ib[BASE224_IN_CHUNK*10], ob[BASE224_OUT_CHUNK*10], *b; + uns il, ol; + + if (argc != 2 || argv[1][0] != '-') + goto usage; + + switch (argv[1][1]) + { + case 'e': /* Plain encoding */ + while (il = bread(in, ib, sizeof(ib))) + { + ol = base224_encode(ob, ib, il); + bwrite(out, ob, ol); + } + break; + case 'E': /* Line block encoding */ + while (il = bread(in, ib, BASE224_IN_CHUNK*6)) + { + ol = base224_encode(ob, ib, il); + bputc(out, 'N'); + bwrite(out, ob, ol); + bputc(out, '\n'); + } + break; + case 'd': /* Plain decoding */ + while (ol = bread(in, ob, sizeof(ob))) + { + il = base224_decode(ib, ob, ol); + bwrite(out, ib, il); + } + break; + case 'D': /* Line block decoding */ + while (b = bgets(in, ob, sizeof(ob))) + { + if (!ob[0]) + die("Invalid line syntax"); + il = base224_decode(ib, ob+1, b-ob-1); + bwrite(out, ib, il); + } + break; + default: + usage: + fputs("Usage: b224 (-e|-E|-d|-D)\n", stderr); + return 1; + } + + bclose(in); + bclose(out); + return 0; +} diff --git a/ucw/daemon-helper.c b/ucw/daemon-helper.c new file mode 100644 index 00000000..6146aabb --- /dev/null +++ b/ucw/daemon-helper.c @@ -0,0 +1,185 @@ +/* + * A Simple Wrapper for Starting and Stopping of Daemons + * + * (c) 2003 Martin Mares + * + * It would seem that we are reinventing the wheel and the + * start-stop-daemon command present in most Linux distributions + * is just what we need, but the usual "does the process already + * exist?" strategies fail in presence of multiple running daemons. + * + * Return codes: + * 101 already running + * 102 not running + */ + +#include "ucw/lib.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum action { + ACTION_NONE, + ACTION_START, + ACTION_STOP, + ACTION_FORCE_STOP, + ACTION_CHECK, + ACTION_RELOAD +}; + +static int action; + +static struct option options[] = { + { "pid-file", required_argument, NULL, 'p' }, + { "status-file", required_argument, NULL, 's' }, + { "start", no_argument, &action, ACTION_START }, + { "stop", no_argument, &action, ACTION_STOP }, + { "force-stop", no_argument, &action, ACTION_FORCE_STOP }, + { "check", no_argument, &action, ACTION_CHECK }, + { "reload", no_argument, &action, ACTION_RELOAD }, + { NULL, no_argument, NULL, 0 } +}; + +static void NONRET +usage(void) +{ + fputs("\n\ +Usage: daemon-helper --start -- \n\ + or: daemon-helper --stop \n\ + or: daemon-helper --force-stop \n\ + or: daemon-helper --reload \n\ + or: daemon-helper --check \n\ +\n\ +Options:\n\ +--pid-file Name of PID file for this daemon (mandatory)\n\ +--status-file Status file used by the daemon (deleted just before starting)\n\ +", stderr); + exit(1); +} + +int +main(int argc, char **argv) +{ + int c, fd; + char *pidfile = NULL; + char *statfile = NULL; + struct flock fl; + char buf[64]; + + while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0) + switch (c) + { + case 0: + break; + case 'p': + pidfile = optarg; + break; + case 's': + statfile = optarg; + break; + default: + usage(); + } + if (!pidfile) + usage(); + + bzero(&fl, sizeof(fl)); + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + + switch (action) + { + case ACTION_START: + if (optind >= argc) + usage(); + fd = open(pidfile, O_RDWR | O_CREAT, 0666); + if (fd < 0) + die("Unable to create %s: %m", pidfile); + if ((c = fcntl(fd, F_SETLK, &fl)) < 0) + { + if (errno == EAGAIN || errno == EACCES) + return 101; + else + die("fcntl lock on %s failed: %m", pidfile); + } + c = sprintf(buf, "%d\n", getpid()); + if (write(fd, buf, c) != c) + die("write on %s failed: %m", pidfile); + if (ftruncate(fd, c) < 0) + die("truncate on %s failed: %m", pidfile); + if (statfile && unlink(statfile) < 0 && errno != ENOENT) + die("unlink(%s) failed: %m", statfile); + setsid(); + /* Disconnect from stdin and stdout, leave stderr to the daemon. */ + close(0); + open("/dev/null", O_RDWR, 0); + dup2(0, 1); + argv += optind; + argc -= optind; + char **a = alloca(sizeof(char *) * (argc+1)); + memcpy(a, argv, sizeof(char *) * argc); + a[argc] = NULL; + execv(a[0], a); + die("Cannot execute %s: %m", a[0]); + case ACTION_STOP: + case ACTION_FORCE_STOP: + case ACTION_CHECK: + case ACTION_RELOAD: + if (optind < argc) + usage(); + fd = open(pidfile, O_RDWR); + if (fd < 0) + { + if (errno == ENOENT) + return 102; + else + die("Unable to open %s: %m", pidfile); + } + if ((c = fcntl(fd, F_SETLK, &fl)) >= 0) + { + nopid: + unlink(pidfile); + return 102; + } + if (errno != EAGAIN && errno != EACCES) + die("fcntl lock on %s failed: %m", pidfile); + if ((c = read(fd, buf, sizeof(buf))) < 0) + die("read on %s failed: %m", pidfile); + if (!c) + goto nopid; + if (c >= (int) sizeof(buf) || sscanf(buf, "%d", &c) != 1) + die("PID file syntax error"); + int sig = 0; + if (action == ACTION_CHECK || action == ACTION_RELOAD) + { + if (action == ACTION_RELOAD) + sig = SIGHUP; + if (kill(c, sig) < 0 && errno == ESRCH) + goto nopid; + return 0; + } + sig = (action == ACTION_STOP) ? SIGTERM : SIGQUIT; + if (kill(c, sig) < 0) + { + if (errno == ESRCH) + goto nopid; + die("Cannot kill process %d: %m", c); + } + if ((c = fcntl(fd, F_SETLKW, &fl)) < 0) + die("Cannot lock %s: %m", pidfile); + if (statfile) + unlink(statfile); + if (unlink(pidfile) < 0) + die("Cannot unlink %s: %m", pidfile); + return 0; + default: + usage(); + } +} diff --git a/ucw/default.cfg b/ucw/default.cfg index 8c2b21ba..cb6e3461 100644 --- a/ucw/default.cfg +++ b/ucw/default.cfg @@ -35,6 +35,9 @@ UnSet("CONFIG_UCW_PERL_MODULES"); # Include support utilities for shell scripts Set("CONFIG_UCW_SHELL_UTILS" => 1); +# Include utilities +Set("CONFIG_UCW_UTILS" => 1); + # Default configuration file UnSet("DEFAULT_CONFIG"); diff --git a/ucw/hex.c b/ucw/hex.c new file mode 100644 index 00000000..4ddb96ab --- /dev/null +++ b/ucw/hex.c @@ -0,0 +1,263 @@ +/* + * Hexadecimal dumper (CP/M style format) + * + * Original version (c) Eric S. Raymond + * Heavily modified by Martin Mares + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include "ucw/lib.h" +#include "ucw/lfs.h" + +#include +#include +#include +#include +#include +#include + +#define DEFWIDTH 16 /* Default # chars to show per line */ +#define MAXWIDTH 32 /* Maximum # of bytes per line */ + +typedef int bool; +#define TRUE 1 +#define FALSE 0 + +static long linesize = DEFWIDTH; /* # of bytes to print per line */ +static bool cflag = FALSE; /* show printables as ASCII if true */ +static bool gflag = FALSE; /* suppress mid-page gutter if true */ +static ucw_off_t start = 0; /* file offset to start dumping at */ +static ucw_off_t length = 0; /* if nz, how many chars to dump */ + +static void dumpfile(FILE *f) + /* dump a single, specified file -- stdin if filename is NULL */ +{ + int ch = '\0'; /* current character */ + char ascii[MAXWIDTH+3]; /* printable ascii data */ + int i = 0; /* counter: # bytes processed */ + int ai = 0; /* index into ascii[] */ + ucw_off_t offset = start; /* byte offset of line in file */ + int hpos = 0; /* horizontal position counter */ + ucw_off_t fstart = start; + ucw_off_t flength = length; + char *specials = "\b\f\n\r\t"; + char *escapes = "bfnrt"; + char *cp; + + if (fstart && ucw_seek(fileno(f), fstart, SEEK_SET) >= 0) + fstart = 0; + + do { + ch = getc(f); + + if (ch != EOF) + { + if (length && flength-- <= 0) + ch = EOF; + } + + if (ch != EOF) + { + if (i++ % linesize == 0) + { + (void) printf("%04Lx ", (long long) offset); + offset += linesize; + hpos = 5; + } + + /* output one space for the mid-page gutter */ + if (!gflag) + if ((i - 1) % (linesize / 2) == 0) + { + (void) putchar(' '); + hpos++; + ascii[ai++] = ' '; + } + + /* dump the indicated representation of a character */ + ascii[ai] = (isprint (ch) || ch == ' ') ? ch : '.'; + + if (cflag && (isprint(ch) || ch == ' ')) + (void) printf("%c ", ch); + else if (cflag && ch && (cp = strchr(specials, ch))) + (void) printf("\\%c ", escapes[cp - specials]); + else + (void) printf("%02x ", ch); + + /* update counters and things */ + ai++; + hpos += 3; + } + + /* At end-of-line or EOF, show ASCII version of data. */ + if (i && (ch == EOF || (i % linesize == 0))) + { + if (!cflag) + { + while (hpos < linesize * 3 + 7) + { + hpos++; + (void) putchar(' '); + } + + ascii[ai] = '\0'; + (void) printf("%s", ascii); + } + + if (ch != EOF || (i % linesize != 0)) + (void) putchar('\n'); + ai = 0; /* reset counters */ + } + } while + (ch != EOF); +} + +static ucw_off_t getoffs(char *cp) + /* fetch decimal or hex integer to be used as file start or offset */ +{ + ucw_off_t value = 0; + char *hexdigits = "0123456789abcdefABCDEF"; + +#if 0 + bool foundzero = FALSE; + int base = 0; + + for (; *cp; cp++) + if (*cp == '0') + foundzero = TRUE; + else if (isdigit(*cp)) + { + base = 10; + break; + } + else if (*cp = 'x' || *cp == 'X' || *cp == 'h' || *cp == 'H') + { + base = 16; + cp++; + break; + } + else + return(-1L); + + if (base == 0) + if (foundzero) + base = 10; + else + return(-1L); + + if (base == 10) + { + for (; *cp; cp++) + if (isdigit(*cp)) + value = value * 10 + (*cp - '0'); + else + return(-1L); + } + else +#endif + { + for (; *cp; cp++) + if (strchr(hexdigits, *cp)) + value = value*16 + (strchr(hexdigits, tolower(*cp))-hexdigits); + else + return -1; + } + + return(value); +} + +int main(int argc, char **argv) +{ + FILE *infile; /* file pointer input file */ + int dumpcount = 0; /* count of files dumped so far */ + char *cp; + int fd; + + for (argv++, argc--; argc > 0; argv++, argc--) + { + char s = **argv; + + if (s == '-' || s == '+') + { + int c = *++*argv; + + switch (c) + { + case 'c': cflag = (s == '-'); continue; + case 'g': gflag = (s == '-'); continue; + + case 's': + if ((*argv)[1]) + (*argv)++; + else + argc--, argv++; + if (s == '-' && argc >= 0) + { + if (cp = strchr(*argv, ',')) + *cp++ = '\0'; + if ((start = getoffs(*argv)) < 0) + { + (void) fputs("hex: start offset no good\n", stderr); + exit(1); + } + + if (cp) + if ((length = getoffs(cp)) < 0) + { + (void) fputs("hex: length no good\n", stderr); + exit(1); + } + } + else + start = length = 0L; + continue; + + case '\0': + infile = stdin; + break; + + case 'w': + if ((*argv)[1]) + (*argv)++; + else + argc--, argv++; + if ((linesize = getoffs(*argv)) == -1L || linesize > MAXWIDTH) + { + (void) fputs("hex: line width no good\n", stderr); + exit(1); + } + if (linesize % 2) + gflag = TRUE; + continue; + + default: + (void) fprintf(stderr, "hex: no such option as %s\n", *argv); + exit(1); + } + } + else + { + fd = ucw_open(*argv, O_RDONLY, 0); + if (fd < 0 || !(infile = fdopen(fd, "r"))) + { + (void) fprintf(stderr, "hex: cannot open %s: %m\n", *argv); + exit(1); + } + } + + if (dumpcount > 0 || argc > 1) + if (infile == stdin) + (void) printf("---- ----\n"); + else + (void) printf("---- %s ----\n", *argv); + dumpfile(infile); + dumpcount++; + if (infile != stdin) + (void) fclose(infile); + } + + if (dumpcount == 0) + dumpfile(stdin); + return(0); +} diff --git a/ucw/rotate-log.pl b/ucw/rotate-log.pl new file mode 100644 index 00000000..e4c9b109 --- /dev/null +++ b/ucw/rotate-log.pl @@ -0,0 +1,27 @@ +#!/usr/bin/perl + +# Rotate Sherlock logs +# (c) 2001--2002 Martin Mares + +use File::stat; + +@ARGV >= 3 or die "Usage: rotate-log "; + +$now = time; +$cps = shift @ARGV; +$del = shift @ARGV; + +$compress_thr = $now - 86400 * $cps; +$delete_thr = $now - 86400 * $del; +foreach $f (@ARGV) { + -f $f or next; + $st = stat $f or next; + if ($del > 0 && $st->mtime < $delete_thr) { + print "Deleting $f\n"; + unlink $f || die "Delete FAILED: $!"; + } elsif ($cps > 0 && $st->mtime < $compress_thr && $f !~ /\.(gz|bz2)$/) { + print "Compressing $f\n"; + `gzip -f $f`; + $? && die "Compression FAILED: $!"; + } +} diff --git a/ucw/urltool.c b/ucw/urltool.c new file mode 100644 index 00000000..5bcb33ca --- /dev/null +++ b/ucw/urltool.c @@ -0,0 +1,145 @@ +/* + * Sherlock Utilities -- URL Handling Tool + * + * (c) 2004 Martin Mares + */ + +#include "ucw/lib.h" +#include "ucw/getopt.h" +#include "ucw/url.h" +#include "ucw/fastbuf.h" + +#include +#include +#include + +static byte *base_url; +static struct url base; +static uns opt_split = 0, opt_normalize = 0, opt_forgive = 0; +static struct fastbuf *fout; +static uns err_count; + +static void +process_url(byte *url) +{ + byte buf1[MAX_URL_SIZE], buf2[MAX_URL_SIZE], buf3[MAX_URL_SIZE], buf4[MAX_URL_SIZE]; + int e; + struct url ur; + + if ((e = url_deescape(url, buf1)) || (e = url_split(buf1, &ur, buf2))) + goto error; + if ((base_url || opt_normalize) && (e = url_normalize(&ur, &base))) + goto error; + if (opt_normalize && (e = url_canonicalize(&ur))) + goto error; + if (opt_split) + { + if (ur.protocol) + bprintf(fout, "protocol=%s\n", ur.protocol); + if (ur.user) + bprintf(fout, "user=%s\n", ur.user); + if (ur.pass) + bprintf(fout, "pass=%s\n", ur.pass); + if (ur.host) + bprintf(fout, "host=%s\n", ur.host); + if (ur.port != ~0U) + bprintf(fout, "port=%d\n", ur.port); + if (ur.rest) + bprintf(fout, "rest=%s\n", ur.rest); + bputc(fout, '\n'); + } + else + { + if ((e = url_pack(&ur, buf3)) || (e = url_enescape(buf3, buf4))) + goto error; + bprintf(fout, "%s\n", buf4); + } + return; + + error: + msg(L_ERROR, "%s: %s", url, url_error(e)); + err_count++; +} + +static char *shortopts = CF_SHORT_OPTS "b:fns"; +static struct option longopts[] = +{ + CF_LONG_OPTS + { "base", 1, 0, 'b' }, + { "forgive", 0, 0, 'f' }, + { "normalize", 0, 0, 'n' }, + { "split", 0, 0, 's' }, + { NULL, 0, 0, 0 } +}; + +static char *help = "\ +Usage: urltool [] []\n\ +\n\ +Options:\n" +CF_USAGE "\ +-b, --base \tInput URL's are relative to this base\n\ +-f, --forgive\t\tReturn exit status 0 even if there were errors\n\ +\n\ +Operations:\n\ +-s, --split\t\tSplit a given URL to components\n\ +-n, --normalize\t\tNormalize given URL\n\ +"; + +static void NONRET +usage(byte *msg) +{ + if (msg) + { + fputs(msg, stderr); + fputc('\n', stderr); + } + fputs(help, stderr); + exit(1); +} + +int +main(int argc, char **argv) +{ + int opt, err; + byte *base_url = NULL; + byte basebuf1[MAX_URL_SIZE], basebuf2[MAX_URL_SIZE]; + + log_init(argv[0]); + while ((opt = cf_getopt(argc, argv, shortopts, longopts, NULL)) >= 0) + switch (opt) + { + case 'b': + base_url = optarg; + err = url_canon_split(base_url, basebuf1, basebuf2, &base); + if (err) + die("Invalid base URL: %s", url_error(err)); + break; + case 's': + opt_split = 1; + break; + case 'n': + opt_normalize = 1; + break; + case 'f': + opt_forgive = 1; + break; + default: + usage("Invalid option"); + } + + fout = bfdopen_shared(1, 4096); + if (optind >= argc) + { + struct fastbuf *fin = bfdopen_shared(0, 4096); + byte url[MAX_URL_SIZE]; + while (bgets(fin, url, sizeof(url))) + process_url(url); + bclose(fin); + } + else + while (optind < argc) + process_url(argv[optind++]); + bclose(fout); + + return (err_count && !opt_forgive); +}