**/
enum daemon_control_status daemon_control(struct daemon_control_params *dc);
-// XXX: Also used as exit codes of the daemon-control utility.
+// XXX: Also used as exit codes of the ucw-daemon-control utility.
enum daemon_control_status {
DAEMON_STATUS_OK = 0,
DAEMON_STATUS_ALREADY_DONE = 100,
# Include utilities
Set("CONFIG_UCW_UTILS" => 1);
-# Include obsolete daemon-helper utility
+# Include obsolete ucw-daemon-helper utility
UnSet("CONFIG_UCW_OBSOLETE_DAEMON_HELPER");
# Default configuration file
The basecode utility
--------------------
You can use the encoding/decoding routines from command line, trough
-`basecode` command. You have to specify the operation by a command
+`ucw-basecode` command. You have to specify the operation by a command
line argument and give it the data on standard input. The arguments
are:
We therefore recommend the following daemon control protocol, which prevents all such
race conditions. Its implementation is available in form of the daemon_control() library
-function or the `daemon-control` stand-alone utility.
+function or the `ucw-daemon-control` stand-alone utility.
* There exist two files:
** PID file (usually `/var/run/daemon.pid`), which contains the PID of the daemon
# Support routines for shell scripts
DIRS+=ucw/shell
-UCW_SHELL_PROGS=$(addprefix $(o)/ucw/shell/,config logger logoutput)
+UCW_SHELL_PROGS=$(addprefix $(o)/ucw/shell/ucw-,config logger logoutput)
PROGS+=$(UCW_SHELL_PROGS)
DATAFILES+=$(o)/ucw/shell/libucw.sh
-$(o)/ucw/shell/config: $(o)/ucw/shell/config.o $(LIBUCW)
-$(o)/ucw/shell/logger: $(o)/ucw/shell/logger.o $(LIBUCW)
-$(o)/ucw/shell/logoutput: $(o)/ucw/shell/logoutput.o $(LIBUCW)
+$(o)/ucw/shell/ucw-config: $(o)/ucw/shell/ucw-config.o $(LIBUCW)
+$(o)/ucw/shell/ucw-logger: $(o)/ucw/shell/ucw-logger.o $(LIBUCW)
+$(o)/ucw/shell/ucw-logoutput: $(o)/ucw/shell/ucw-logoutput.o $(LIBUCW)
TESTS+=$(addprefix $(o)/ucw/shell/,config.test)
-$(o)/ucw/shell/config.test: $(o)/ucw/shell/config
+$(o)/ucw/shell/config.test: $(o)/ucw/shell/ucw-config
INSTALL_TARGETS+=install-ucw-shell
install-ucw-shell:
+++ /dev/null
-/*
- * UCW Library -- Shell Interface to Configuration Files
- *
- * (c) 2002--2005 Martin Mares <mj@ucw.cz>
- * (c) 2006 Robert Spalek <robert@ucw.cz>
- * (c) 2006 Pavel Charvat <pchar@ucw.cz>
- *
- * Once we were using this beautiful Shell version, but it turned out
- * that it doesn't work with nested config files:
- *
- * eval `sed <cf/sherlock '/^#/d;/^ *$/d;s/ \+$//;
- * h;s@[^ ]*@@;x;s@[ ].*@@;y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/;G;s/\n//;
- * /^\[SECTION\]/,/^\[/ {; /^[A-Z]/ { s/^\([^ ]\+\)[ ]*\(.*\)$/SH_\1="\2"/; p; }; };
- * d;'`
- *
- * This software may be freely distributed and used according to the terms
- * of the GNU Lesser General Public License.
- */
-
-#include <ucw/lib.h>
-#include <ucw/conf.h>
-#include <ucw/getopt.h>
-#include <ucw/conf-internal.h>
-#include <ucw/clists.h>
-#include <ucw/mempool.h>
-#include <ucw/chartype.h>
-#include <ucw/bbuf.h>
-#include <ucw/string.h>
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <alloca.h>
-
-static void
-help(void)
-{
- fputs("\n\
-Usage: config [-C<configfile>] [-S<section>.<option>=<value>] <sections>\n\
-\n\
-<sections>\t<section>[;<sections>]\n\
-<section>\t[!]<name>{[<items>]}\n\
-<items>\t\t[-]<item>[;<items>]\n\
-<item>\t\t<static> | <array> | <list>\n\
-<static>\t<type><name>[=<value>]\n\
-<list>\t\t@<name>{[<items>]}\n\
-<array>\t\t<type><name><left-bracket>[<number>]<right-bracket>\n\
-<value>\t\t[a-zA-Z0-9.-/]* | 'string without single quotes'<value> | \"c-like string\"<value>\n\
-\n\
-Types:\n\
-<empty>\t\tString\n\
-#\t\t32-bit integer\n\
-##\t\t64-bit integer\n\
-$\t\tFloating point number\n\
-\n\
-Modifiers:\n\
-!\t\tReport unknown items as errors\n\
--\t\tDo not dump item's value\n\
-", stderr);
- exit(1);
-}
-
-union value {
- void *v_ptr;
- int v_int;
- u64 v_u64;
- double v_double;
- clist list;
-};
-
-#define FLAG_HIDE 0x1
-#define FLAG_NO_UNKNOWN 0x2
-
-struct item {
- cnode node;
- uns flags;
- struct cf_item cf;
- union value value;
- uns index;
-};
-
-struct section {
- struct item item;
- clist list;
- uns count;
- uns size;
-};
-
-static struct mempool *pool;
-static clist sections;
-static byte *pos;
-
-static void
-parse_white(void)
-{
- while (Cspace(*pos))
- pos++;
-}
-
-static void
-parse_char(byte c)
-{
- if (*pos++ != c)
- die("Missing '%c'", c);
-}
-
-static byte *
-parse_name(void)
-{
- byte *name = pos;
- while (Cword(*pos))
- pos++;
- uns len = pos - name;
- if (!len)
- die("Expected item/section name");
- byte *buf = mp_alloc(pool, len + 1);
- memcpy(buf, name, len);
- buf[len] = 0;
- return buf;
-}
-
-static void
-parse_section(struct section *section)
-{
-#define TRY(x) do{byte *_err=(x); if (_err) die(_err); }while(0)
- for (uns sep = 0; ; sep = 1)
- {
- parse_white();
- if (!*pos || *pos == '}')
- break;
- if (sep)
- parse_char(';');
- parse_white();
-
- struct item *item;
-
- if (*pos == '@')
- {
- pos++;
- struct section *sec = mp_alloc_zero(pool, sizeof(*sec));
- sec->size = sizeof(cnode);
- clist_init(&sec->list);
- item = &sec->item;
- item->cf.name = parse_name();
- item->cf.cls = CC_LIST;
- item->cf.number = 1;
- parse_white();
- parse_char('{');
- parse_section(sec);
- parse_char('}');
- }
- else
- {
- item = mp_alloc_zero(pool, sizeof(*item));
- if (*pos == '-')
- {
- item->flags |= FLAG_HIDE;
- pos++;
- }
- item->cf.cls = CC_STATIC;
- item->cf.number = 1;
- switch (*pos)
- {
- case '#':
- if (*++pos == '#')
- {
- pos++;
- item->cf.type = CT_U64;
- }
- else
- item->cf.type = CT_INT;
- break;
- case '$':
- pos++;
- item->cf.type = CT_DOUBLE;
- break;
- default:
- if (!Cword(*pos))
- die("Invalid type syntax");
- item->cf.type = CT_STRING;
- break;
- }
- parse_white();
- item->cf.name = parse_name();
- parse_white();
- if (*pos == '[')
- {
- pos++;
- parse_white();
- item->cf.cls = CC_DYNAMIC;
- byte *num = pos;
- while (*pos && *pos != ']')
- pos++;
- if (!*pos)
- die("Missing ']'");
- *pos++ = 0;
- if (!*num)
- item->cf.number = CF_ANY_NUM;
- else
- {
- int inum;
- TRY(cf_parse_int(num, &inum));
- if (!inum)
- die("Invalid array length");
- item->cf.number = inum;
- }
- parse_white();
- }
- if (*pos == '=')
- {
- pos++;
- parse_white();
- if (section->item.cf.cls == CC_LIST)
- die("List items can not have default values");
- if (item->cf.cls == CC_DYNAMIC)
- die("Arrays can not have default values");
- byte *def = pos, *d = def;
- while (*pos != ';' && *pos != '}' && !Cspace(*pos))
- {
- if (*pos == '\'')
- {
- pos++;
- while (*pos != '\'')
- {
- if (!*pos)
- die("Unterminated string");
- *d++ = *pos++;
- }
- pos++;
- }
- else if (*pos == '"')
- {
- pos++;
- byte *start = d;
- uns esc = 0;
- while (*pos != '"' || esc)
- {
- if (!*pos)
- die("Unterminated string");
- if (*pos == '\\')
- esc ^= 1;
- else
- esc = 0;
- *d++ = *pos++;
- }
- pos++;
- *d = 0;
- d = str_unesc(start, start);
- }
- else
- *d++ = *pos++;
- }
- uns len = d - def;
- byte *buf = mp_alloc(pool, len + 1);
- memcpy(buf, def, len);
- buf[len] = 0;
- switch (item->cf.type)
- {
- case CT_STRING:
- item->value.v_ptr = buf;
- break;
- case CT_INT:
- TRY(cf_parse_int(buf, &item->value.v_int));
- break;
- case CT_U64:
- TRY(cf_parse_u64(buf, &item->value.v_u64));
- break;
- case CT_DOUBLE:
- TRY(cf_parse_double(buf, &item->value.v_double));
- break;
- default:
- ASSERT(0);
- }
- }
- }
- if (section->item.cf.cls == CC_LIST)
- {
- item->cf.ptr = (void *)(uintptr_t)section->size;
- section->size += sizeof(union value);
- }
- else
- item->cf.ptr = &item->value;
- clist_add_tail(§ion->list, &item->node);
- section->count++;
- }
-#undef TRY
-}
-
-static void
-parse_outer(void)
-{
- for (uns sep = 0; ; sep = 1)
- {
- parse_white();
- if (!*pos)
- break;
- if (sep)
- parse_char(';');
- parse_white();
- struct section *sec = mp_alloc_zero(pool, sizeof(*sec));
- if (*pos == '!')
- {
- pos++;
- sec->item.flags |= FLAG_NO_UNKNOWN;
- }
- sec->item.cf.name = parse_name();
- parse_white();
- parse_char('{');
- clist_add_tail(§ions, &sec->item.node);
- clist_init(&sec->list);
- parse_section(sec);
- parse_char('}');
- }
-}
-
-static struct cf_section *
-generate_section(struct section *section)
-{
- struct cf_section *sec = mp_alloc_zero(pool, sizeof(*sec));
- if (section->item.cf.cls == CC_LIST)
- sec->size = section->size;
- struct cf_item *c = sec->cfg = mp_alloc_zero(pool, sizeof(struct cf_item) * (section->count + 1));
- CLIST_FOR_EACH(struct item *, item, section->list)
- {
- *c = item->cf;
- if (c->cls == CC_LIST)
- c->u.sec = generate_section((struct section *)item);
- c++;
- }
- c->cls = CC_END;
- return sec;
-}
-
-static bb_t path;
-
-static void
-dump_value(uns array, struct item *item, void *v)
-{
- byte buf[128], *value = buf;
- if (!array)
- printf("CF_%s_%s='", path.ptr, item->cf.name);
- else
- printf("CF_%s_%s[%u]='", path.ptr, item->cf.name, ++item->index);
- switch (item->cf.type)
- {
- case CT_INT:
- sprintf(buf, "%d", *(int *)v);
- break;
- case CT_U64:
- sprintf(buf, "%llu", (long long) *(u64 *)v);
- break;
- case CT_DOUBLE:
- sprintf(buf, "%g", *(double *)v);
- break;
- case CT_STRING:
- if (*(byte **)v)
- value = *(byte **)v;
- else
- *value = 0;
- break;
- default:
- ASSERT(0);
- }
- while (*value) {
- if (*value == '\'')
- printf("'\\''");
- else
- putchar(*value);
- value++;
- }
- printf("'\n");
-}
-
-static void
-dump_item(struct item *item, void *ptr, uns path_len)
-{
- if (item->flags & FLAG_HIDE)
- return;
- byte *val = (byte *)((uintptr_t)ptr + (uintptr_t)item->cf.ptr);
- if (item->cf.cls == CC_LIST)
- {
- uns len = strlen(item->cf.name);
- bb_grow(&path, path_len + len + 1);
- path.ptr[path_len] = '_';
- memcpy(path.ptr + path_len + 1, item->cf.name, len);
- CLIST_FOR_EACH(cnode *, ptr2, *(clist *)val)
- CLIST_FOR_EACH(struct item *, item2, ((struct section *)item)->list)
- dump_item(item2, ptr2, path_len + len + 1);
- }
- else
- {
- bb_grow(&path, path_len + 1)[path_len] = 0;
- if (item->cf.cls == CC_STATIC)
- dump_value(!!ptr, item, val);
- else
- {
- val = *(void **)val;
- uns len = DARY_LEN(val);
- uns size = cf_type_size(item->cf.type, NULL);
- for (uns i = 0; i < len; i++, val += size)
- dump_value(1, item, val);
- }
- }
-}
-
-int main(int argc, char **argv)
-{
- log_init("config");
- if (argc < 2)
- help();
- pos = argv[argc - 1];
- argv[argc - 1] = NULL;
-
- pool = mp_new(0x1000);
- clist_init(§ions);
- parse_outer();
- CLIST_FOR_EACH(struct section *, sec, sections)
- cf_declare_section(sec->item.cf.name, generate_section(sec), !(sec->item.flags & FLAG_NO_UNKNOWN));
-
- if (cf_getopt(argc - 1, argv, CF_SHORT_OPTS, CF_NO_LONG_OPTS, NULL) != -1)
- help();
-
- bb_init(&path);
- CLIST_FOR_EACH(struct section *, section, sections)
- {
- uns len = strlen(section->item.cf.name);
- memcpy(bb_grow(&path, len), section->item.cf.name, len);
- CLIST_FOR_EACH(struct item *, item, section->list)
- dump_item(item, NULL, len);
- }
- bb_done(&path);
-
- return 0;
-}
-
# Tests for configuration parser
-Run: ../obj/ucw/shell/config -C/dev/null -S 'sec1{int1=23; long1=1234567812345678; long2=4321; str1="s1"; str2="s2"}' 'sec1 {#int1; ##long1; -str1; str2; #int2=123; ##long2=1234; #int3=0x10; #int4; $dbl1=001.100; $dbl2}; sec2{str3}'
+Run: ../obj/ucw/shell/ucw-config -C/dev/null -S 'sec1{int1=23; long1=1234567812345678; long2=4321; str1="s1"; str2="s2"}' 'sec1 {#int1; ##long1; -str1; str2; #int2=123; ##long2=1234; #int3=0x10; #int4; $dbl1=001.100; $dbl2}; sec2{str3}'
Out: CF_sec1_int1='23'
CF_sec1_long1='1234567812345678'
CF_sec1_str2='s2'
CF_sec1_dbl2='0'
CF_sec2_str3=''
-Run: ../obj/ucw/shell/config -C/dev/null -S 'sec1{list1 1 a1 b1; list1:clear; list1 2 a2 b2 3 a3 b3}' 'sec1 {@list1 {#int1; str1; -str2}}'
+Run: ../obj/ucw/shell/ucw-config -C/dev/null -S 'sec1{list1 1 a1 b1; list1:clear; list1 2 a2 b2 3 a3 b3}' 'sec1 {@list1 {#int1; str1; -str2}}'
Out: CF_sec1_list1_int1[1]='2'
CF_sec1_list1_str1[1]='a2'
CF_sec1_list1_int1[2]='3'
CF_sec1_list1_str1[2]='a3'
-Run: ../obj/ucw/shell/config -C/dev/null -S 'sec1{ar1 a b c d; ar1 a b c; ar2 1 2; ar3 1.1}' 'sec1 {ar1[]; #ar2[2]; $ar3[-2]}'
+Run: ../obj/ucw/shell/ucw-config -C/dev/null -S 'sec1{ar1 a b c d; ar1 a b c; ar2 1 2; ar3 1.1}' 'sec1 {ar1[]; #ar2[2]; $ar3[-2]}'
Out: CF_sec1_ar1[1]='a'
CF_sec1_ar1[2]='b'
CF_sec1_ar1[3]='c'
CF_sec1_ar2[2]='2'
CF_sec1_ar3[1]='1.1'
-Run: ../obj/ucw/shell/config -C/dev/null -S 'sec1{list1 {str1=1; list2=a b c}; list1 {str1=2; list2=d e}}' 'sec1 {@list1 {str1; @list2{str2}}}'
+Run: ../obj/ucw/shell/ucw-config -C/dev/null -S 'sec1{list1 {str1=1; list2=a b c}; list1 {str1=2; list2=d e}}' 'sec1 {@list1 {str1; @list2{str2}}}'
Out: CF_sec1_list1_str1[1]='1'
CF_sec1_list1_list2_str2[1]='a'
CF_sec1_list1_list2_str2[2]='b'
CF_sec1_list1_list2_str2[4]='d'
CF_sec1_list1_list2_str2[5]='e'
-Run: ../obj/ucw/shell/config -C/dev/null 'sec{str=a'\''b"c'\''d"\\e'\''f"g}'
+Run: ../obj/ucw/shell/ucw-config -C/dev/null 'sec{str=a'\''b"c'\''d"\\e'\''f"g}'
Out: CF_sec_str='ab"cd\e'\''fg'
function log # msg
{
- bin/logger $UCW_PROGNAME I "$1"
+ bin/ucw-logger $UCW_PROGNAME I "$1"
}
function errlog # msg
{
- bin/logger $UCW_PROGNAME E "$1"
+ bin/ucw-logger $UCW_PROGNAME E "$1"
}
function warnlog # msg
{
- bin/logger $UCW_PROGNAME E "$1"
+ bin/ucw-logger $UCW_PROGNAME E "$1"
}
function die # msg
{
- bin/logger $UCW_PROGNAME ! "$1"
+ bin/ucw-logger $UCW_PROGNAME ! "$1"
exit 1
}
function parse-config # section vars...
{
- eval `bin/config$UCW_CF "$@"`
+ eval `bin/ucw-config$UCW_CF "$@"`
}
+++ /dev/null
-/*
- * UCW Library Utilities -- A Simple Logger for use in shell scripts
- *
- * (c) 2001--2009 Martin Mares <mj@ucw.cz>
- *
- * This software may be freely distributed and used according to the terms
- * of the GNU Lesser General Public License.
- */
-
-#include <ucw/lib.h>
-#include <ucw/log.h>
-
-#include <stdio.h>
-#include <string.h>
-
-int
-main(int argc, char **argv)
-{
- byte buf[1024], *c;
-
- log_init("logger");
- if (argc < 3 || argc > 4 || strlen(argv[2]) != 1)
- die("Usage: logger [<logname>:]<progname> <level> [<text>]");
- if (c = strchr(argv[1], ':'))
- {
- *c++ = 0;
- log_init(c);
- log_file(argv[1]);
- }
- else
- log_init(argv[1]);
-
- uns level = 0;
- while (level < L_MAX && LS_LEVEL_LETTER(level) != argv[2][0])
- level++;
- if (level >= L_MAX)
- die("Unknown logging level `%s'", argv[2]);
-
- if (argc > 3)
- msg(level, argv[3]);
- else
- while (fgets(buf, sizeof(buf), stdin))
- {
- c = strchr(buf, '\n');
- if (c)
- *c = 0;
- msg(level, buf);
- }
- return 0;
-}
+++ /dev/null
-/*
- * UCW Library Utilities -- A Simple Logger for use in shell scripts
- *
- * (c) 2001--2009 Martin Mares <mj@ucw.cz>
- * (c) 2011 Tomas Ebenlendr <ebik@ucw.cz>
- *
- * This software may be freely distributed and used according to the terms
- * of the GNU Lesser General Public License.
- */
-
-#undef LOCAL_DEBUG
-
-#include <ucw/lib.h>
-#include <ucw/log.h>
-#include <ucw/mainloop.h>
-#include <ucw/clists.h>
-#include <ucw/getopt.h>
-#include <ucw/conf.h>
-#include <ucw/process.h>
-
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <errno.h>
-
-static uns max_line = 1024;
-static int launch_finish_messages = 1;
-static int nonzero_status_message = 1;
-
-static struct cf_section cfsec_logoutput = {
- CF_ITEMS {
- CF_UNS("LineMax", &max_line),
- CF_END
- }
-};
-
-static clist filedescriptors;
-
-struct fds {
- cnode node;
- int pipe[2];
- int fdnum;
- uns level;
- int long_continue;
- struct main_rec_io rio;
-};
-
-static void
-close_fd(struct fds *fd)
-{
- rec_io_del(&fd->rio);
- close(fd->fdnum);
- clist_remove(&fd->node);
- if (clist_empty(&filedescriptors))
- main_shut_down();
-}
-
-
-static void
-do_msg (struct fds *fd, char *l_msg, int long_continue)
-{
- msg(fd->level, "%s%s", (fd->long_continue ? "... " : ""), l_msg);
- fd->long_continue = long_continue;
-}
-
-static uns
-handle_read(struct main_rec_io *r)
-{
- char buf[max_line + 5];
- byte *eol = memchr((char *)r->read_rec_start + r->read_prev_avail, '\n', r->read_avail - r->read_prev_avail);
- if (eol == NULL) {
- if (r->read_avail >= max_line) {
- memcpy(buf, r->read_rec_start, max_line);
- memcpy(buf + max_line, " ...", 5);
- do_msg(r->data, buf, 1);
- return max_line;
- } else
- return 0;
- }
- *eol = 0;
- byte *b = r->read_rec_start;
- while ((uns)(eol - b) > max_line) {
- char cc = b[max_line];
- b[max_line]=0;
- do_msg(r->data, b, 1);
- b[max_line]=cc;
- b+=max_line;
- }
- do_msg(r->data, (char *)b, 0);
- return eol - r->read_rec_start + 1;
-}
-
-static int
-handle_notify(struct main_rec_io *r, int status)
-{
- struct fds *fd = r->data;
- switch (status) {
- case RIO_ERR_READ:
- case RIO_EVENT_EOF:
- if (r->read_avail) {
- char buf[max_line + 10];
- memcpy(buf, r->read_rec_start, r->read_avail);
- memcpy(buf + r->read_avail, " [no eol]", 10);
- do_msg(r->data, buf, 0);
- } else if (fd->long_continue) {
- do_msg(r->data, "[no eol]", 0);
- }
- close_fd(fd);
- return HOOK_DONE;
- default:
- ASSERT(0);
- }
- return HOOK_IDLE;
-}
-
-
-static void
-add_level_fd(int fdnum, int level)
-{
- struct fds *fd = xmalloc_zero(sizeof(*fd));
- fd->level = level;
- fd->pipe[0] = -1;
- fd->pipe[1] = -1;
- fd->fdnum = fdnum;
- fd->rio.read_handler = handle_read;
- fd->rio.data = fd;
- fd->rio.notify_handler = handle_notify;
- fd->long_continue = 0;
- clist_add_tail(&filedescriptors, &fd->node);
-}
-
-static int
-xdup(int fd)
-{
- int rfd = dup(fd);
- if (rfd == -1)
- die("Cannot dup(): %m");
- DBG(" Dup(%i) -> %i", fd, rfd);
- return rfd;
-}
-
-static int
-xdup2(int fd1, int fd2)
-{
- int rfd = dup2(fd1, fd2);
- if (rfd == -1)
- die("Cannot dup2(): %m");
- DBG(" Dup2(%i, %i) -> %i", fd1, fd2, rfd);
- return rfd;
-}
-
-static void
-xdupavoid(int *fd1, int fd2)
-{
- DBG("Dupavoid: !%i -> %i", fd2, *fd1);
- int ofd = *fd1;
- if (ofd == fd2) {
- *fd1 = xdup(ofd);
- DBG(" Close: %i", ofd);
- close(ofd);
- }
-}
-
-static void
-xdupto(int *fd1, int fd2)
-{
- DBG("Dupto: %i -> %i", *fd1, fd2);
- if (*fd1 == fd2)
- return;
- DBG(" Close: %i", fd2);
- close(fd2);
- xdup2(*fd1, fd2);
- DBG(" Close: %i", *fd1);
- close(*fd1);
- *fd1 = fd2;
-}
-
-static void
-set_cloexec_flag(int fd, int value)
-{
- int flags = fcntl(fd, F_GETFD, 0);
- if (flags < 0)
- die("fcntl(..., F_GETFD, ...) : %m");
- flags = (value) ? flags | FD_CLOEXEC : flags & ~FD_CLOEXEC;
- if (fcntl(fd, F_SETFD, flags) < 0)
- die("fcntl(..., F_SETFD, ...) : %m");
-}
-
-/* The "+" stands for end at first argument (i.e. do not parse options after
- first argument.) */
-#define MY_SHORT_OPTS "+" CF_SHORT_OPTS "f:n:l:ih"
-const struct option my_long_opts[] = {
- CF_LONG_OPTS
- { "help", 0, 0, 'h'},
- { "input", 0, 0, 'i'},
- { "logfile", 1, 0, 'f'},
- { "logname", 1, 0, 'n'},
- { "descriptor", 1, 0, 'l'},
- { "nv", 0, 0, 'q'},
- { "nonverbose", 0, 0, 'q'},
- { "non-verbose", 0, 0, 'q'},
- { "non_verbose", 0, 0, 'q'},
- { "silent", 0, 0, 's'},
- { NULL, 0, 0, 0}
-};
-
-#undef CF_USAGE_TAB
-#define CF_USAGE_TAB "\t "
-static char usage[] =
- "Usage:\n"
- "logoutput -h|--help\t\t This help.\n"
- "logoutput <options> -i|--input\t Read file descriptors and log them.\n"
- "\t\t\t\t default: stdin at level I.\n"
- "logoutput <opts> [--] <cmd> [arguments for cmd ...]\n"
- "\t\t\t\t Open file descriptors for writing for command <cmd> and log them.\n"
- "\t\t\t\t default: stdout:I, stderr:W.\n\n"
- "Options:\n"
- CF_USAGE
- "-n, --logname <name>\t\t Use <name> as program name in logs.\n"
- "-l, --descriptor <fdnum>:<level> Open file descriptor <fdnum> and log it at level <level> (replaces defaults).\n"
- "-f, --logfile <logfile>\t\t Log to file <logfile>.\n"
- "-q, --nv, --nonverbose\t\t Suppress launching and successful finish messages.\n"
- "-s, --silent\t\t\t Suppress launching message and all finish messages.\n"
- "\t\t\t\t (i.e., no warning if it terminates with a nonzero exit code or by a signal)\n";
-
-int
-main(int argc, char **argv)
-{
- int register_default = 1;
- int loginput = 0;
- char *logfile = NULL;
- char *logname = NULL;
- struct fds *stderrfd = NULL;
- int help = 0;
-
- log_init("logoutput");
- clist_init(&filedescriptors);
- cf_declare_section("LogOutput", &cfsec_logoutput, 0);
-
- while (1) {
- int opt = cf_getopt(argc, argv, MY_SHORT_OPTS, my_long_opts, NULL);
- switch (opt) {
- case -1:
- goto opt_done;
-
- case 'h':
- help = 1;
- break;
-
- case 'i':
- loginput = 1;
- break;
-
- case 'f':
- logfile = optarg;
- break;
-
- case 'n':
- logname = optarg;
- break;
-
- case 'l':
- {
- char *c = optarg;
-
- register_default = 0;
- int fdnum = 0;
- int parseerror = 0;
- if ( (c[0]<'0') || (c[0] > '9') )
- parseerror = 1;
- while ( (!parseerror) && (c[0] >= '0') && (c[0] <= '9') )
- { fdnum = fdnum*10 + c[0] - '0'; c++; }
- if ( (!parseerror) && (c[0] != ':') )
- parseerror = 1;
- c++;
- if ( (!parseerror) && (c[0] == 0) )
- parseerror = 1;
- if ( (!parseerror) && (c[1] != 0) )
- parseerror = 1;
- if (parseerror) die("Bad argument `%s' to -l, expects number:letter.", optarg);
-
- uns level = 0;
- while (level < L_MAX && LS_LEVEL_LETTER(level) != c[0])
- level++;
- if (level >= L_MAX)
- die("Unknown logging level `%s'", c);
-
- add_level_fd(fdnum, level);
- }
- break;
-
- case 's':
- nonzero_status_message = 0;
- /* fallthrough */
- case 'q':
- launch_finish_messages = 0;
- break;
-
- default:
- optind--;
- goto opt_done;
- }
- }
-opt_done:
-
- if (!help) {
- if (loginput && (optind < argc))
- die("No cmd is allowed for -i. Use -h for help.");
-
- if ((!loginput) && (optind >= argc)) {
- msg(L_FATAL, "Either command or --input expected.");
- help = 2;
- }
- }
- if (help) {
- write(2, usage, sizeof(usage));
- return (help == 1) ? 0 : 1;
- }
-
- if (register_default) {
- if (loginput) {
- add_level_fd(0, L_INFO);
- } else {
- add_level_fd(1, L_INFO);
- add_level_fd(2, L_WARN);
- }
- }
-
- if (loginput) {
- /* Just check, that we don't want open stderr for reading. */
- CLIST_FOR_EACH(struct fds *, fd, filedescriptors) {
- if (fd->fdnum == 2)
- die("Stderr is reserved for output");
- }
- } else {
- /* Open all filedescriptors and their duplicates. */
- CLIST_FOR_EACH(struct fds *, fd, filedescriptors) {
- CLIST_FOR_EACH(struct fds *, fdcheck, filedescriptors) {
- /* We do a dummy check for collisions of filedescriptors. */
- if (fdcheck == fd)
- break;
- if (fdcheck->fdnum == fd->fdnum) {
- die("Duplicate filedescriptor %i", fd->fdnum);
- }
- xdupavoid(fdcheck->pipe + 0, fd->fdnum);
- xdupavoid(fdcheck->pipe + 1, fd->fdnum);
- }
- if (pipe(fd->pipe) == -1)
- die("Cannot create pipe: %m");
- DBG("Pipe [%i, %i] for %i", fd->pipe[0], fd->pipe[1], fd->fdnum);
- xdupavoid(fd->pipe + 0, fd->fdnum);
-
- if (fd->fdnum == 2) {
- stderrfd = fd; //We need to redirect stderr later.
- } else {
- xdupto(fd->pipe + 1, fd->fdnum);
- }
- DBG("---> [%i, %i] for %i", fd->pipe[0], fd->pipe[1], fd->fdnum);
- set_cloexec_flag(fd->pipe[0], 1);
- set_cloexec_flag(fd->pipe[1], 0);
- }
- }
-
- /* Initialize main loop. */
- main_init();
-
- CLIST_FOR_EACH(struct fds *, fd, filedescriptors) {
- /* Our pipe is created, let fd->fdnum be the reading end. */
- if (!loginput)
- fd->fdnum = fd->pipe[0];
- fd->rio.read_rec_max = max_line + 1;
- rec_io_add(&fd->rio, fd->fdnum);
- }
-
- int pid = -1;
- if (!loginput) {
- /* Launch the child and close filedescriptors. */
- pid = fork();
- if (pid == -1)
- die("Cannot fork: %m");
- if (pid == 0 ) {
- /* Child */
-
- /* Move stderr where it should be. */
- if (stderrfd)
- xdupto(stderrfd->pipe + 1, 2);
-
- execvp(argv[optind], argv + optind);
- if (stderrfd) {
- /* We translate stderr, just print. */
- perror("Cannot exec child");
- return 127;
- }
- /* No stderr translation: use logging function. */
- die("Cannot exec child: %m");
- }
-
- /* Close writing filedescriptors. */
- CLIST_FOR_EACH(struct fds *, fd, filedescriptors) {
- close(fd->pipe[1]);
- }
- }
-
- /* Open logfile or stderr. */
- if (logfile) {
- log_file(logfile);
- close(2);
- }
-
- if (!loginput) {
- /* Inform about launching of the command. */
- int buflen = 0;
- for (int i = optind; i < argc; i++) buflen += strlen(argv[i]) + 1;
- char *buf = xmalloc(buflen);
- char *buf2 = buf;
- for (int i = optind; i < argc; i++) {
- strcpy(buf2, argv[i]);
- buf2 += strlen(argv[i]);
- buf2[0] = ' ';
- buf2++;
- }
- buf2[-1] = 0;
-
- if (launch_finish_messages)
- msg(L_INFO, "Launching command: %s", buf);
- }
-
- /* Set logname. */
- if (logname)
- log_init(logname);
- else if (!loginput)
- log_init(argv[optind]);
-
- /* Start reading from pipes. */
- CLIST_FOR_EACH(struct fds *, fd, filedescriptors)
- rec_io_start_read(&fd->rio);
- main_loop();
-
- if (!loginput) {
- /* Unset logname. */
- log_init("logoutput");
-
- /* Wait for status of the child and inform about finish. */
- int status;
- char buf[256];
-
- while (waitpid(pid, &status, 0) == -1) {
- if (errno != EINTR)
- die("Cannot wait for child: %m");
- }
-
- if (format_exit_status(buf, status)) {
- if (nonzero_status_message)
- msg(L_WARN, "Child %s", buf);
- return WIFEXITED(status) ? WEXITSTATUS(status) : 127;
- } else {
- if (launch_finish_messages)
- msg(L_INFO, "Child terminated successfully.");
- return 0;
- }
- }
-
- return 0;
-}
--- /dev/null
+/*
+ * UCW Library -- Shell Interface to Configuration Files
+ *
+ * (c) 2002--2005 Martin Mares <mj@ucw.cz>
+ * (c) 2006 Robert Spalek <robert@ucw.cz>
+ * (c) 2006 Pavel Charvat <pchar@ucw.cz>
+ *
+ * Once we were using this beautiful Shell version, but it turned out
+ * that it doesn't work with nested config files:
+ *
+ * eval `sed <cf/sherlock '/^#/d;/^ *$/d;s/ \+$//;
+ * h;s@[^ ]*@@;x;s@[ ].*@@;y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/;G;s/\n//;
+ * /^\[SECTION\]/,/^\[/ {; /^[A-Z]/ { s/^\([^ ]\+\)[ ]*\(.*\)$/SH_\1="\2"/; p; }; };
+ * d;'`
+ *
+ * This software may be freely distributed and used according to the terms
+ * of the GNU Lesser General Public License.
+ */
+
+#include <ucw/lib.h>
+#include <ucw/conf.h>
+#include <ucw/getopt.h>
+#include <ucw/conf-internal.h>
+#include <ucw/clists.h>
+#include <ucw/mempool.h>
+#include <ucw/chartype.h>
+#include <ucw/bbuf.h>
+#include <ucw/string.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <alloca.h>
+
+static void
+help(void)
+{
+ fputs("\n\
+Usage: ucw-config [-C<configfile>] [-S<section>.<option>=<value>] <sections>\n\
+\n\
+<sections>\t<section>[;<sections>]\n\
+<section>\t[!]<name>{[<items>]}\n\
+<items>\t\t[-]<item>[;<items>]\n\
+<item>\t\t<static> | <array> | <list>\n\
+<static>\t<type><name>[=<value>]\n\
+<list>\t\t@<name>{[<items>]}\n\
+<array>\t\t<type><name><left-bracket>[<number>]<right-bracket>\n\
+<value>\t\t[a-zA-Z0-9.-/]* | 'string without single quotes'<value> | \"c-like string\"<value>\n\
+\n\
+Types:\n\
+<empty>\t\tString\n\
+#\t\t32-bit integer\n\
+##\t\t64-bit integer\n\
+$\t\tFloating point number\n\
+\n\
+Modifiers:\n\
+!\t\tReport unknown items as errors\n\
+-\t\tDo not dump item's value\n\
+", stderr);
+ exit(1);
+}
+
+union value {
+ void *v_ptr;
+ int v_int;
+ u64 v_u64;
+ double v_double;
+ clist list;
+};
+
+#define FLAG_HIDE 0x1
+#define FLAG_NO_UNKNOWN 0x2
+
+struct item {
+ cnode node;
+ uns flags;
+ struct cf_item cf;
+ union value value;
+ uns index;
+};
+
+struct section {
+ struct item item;
+ clist list;
+ uns count;
+ uns size;
+};
+
+static struct mempool *pool;
+static clist sections;
+static byte *pos;
+
+static void
+parse_white(void)
+{
+ while (Cspace(*pos))
+ pos++;
+}
+
+static void
+parse_char(byte c)
+{
+ if (*pos++ != c)
+ die("Missing '%c'", c);
+}
+
+static byte *
+parse_name(void)
+{
+ byte *name = pos;
+ while (Cword(*pos))
+ pos++;
+ uns len = pos - name;
+ if (!len)
+ die("Expected item/section name");
+ byte *buf = mp_alloc(pool, len + 1);
+ memcpy(buf, name, len);
+ buf[len] = 0;
+ return buf;
+}
+
+static void
+parse_section(struct section *section)
+{
+#define TRY(x) do{byte *_err=(x); if (_err) die(_err); }while(0)
+ for (uns sep = 0; ; sep = 1)
+ {
+ parse_white();
+ if (!*pos || *pos == '}')
+ break;
+ if (sep)
+ parse_char(';');
+ parse_white();
+
+ struct item *item;
+
+ if (*pos == '@')
+ {
+ pos++;
+ struct section *sec = mp_alloc_zero(pool, sizeof(*sec));
+ sec->size = sizeof(cnode);
+ clist_init(&sec->list);
+ item = &sec->item;
+ item->cf.name = parse_name();
+ item->cf.cls = CC_LIST;
+ item->cf.number = 1;
+ parse_white();
+ parse_char('{');
+ parse_section(sec);
+ parse_char('}');
+ }
+ else
+ {
+ item = mp_alloc_zero(pool, sizeof(*item));
+ if (*pos == '-')
+ {
+ item->flags |= FLAG_HIDE;
+ pos++;
+ }
+ item->cf.cls = CC_STATIC;
+ item->cf.number = 1;
+ switch (*pos)
+ {
+ case '#':
+ if (*++pos == '#')
+ {
+ pos++;
+ item->cf.type = CT_U64;
+ }
+ else
+ item->cf.type = CT_INT;
+ break;
+ case '$':
+ pos++;
+ item->cf.type = CT_DOUBLE;
+ break;
+ default:
+ if (!Cword(*pos))
+ die("Invalid type syntax");
+ item->cf.type = CT_STRING;
+ break;
+ }
+ parse_white();
+ item->cf.name = parse_name();
+ parse_white();
+ if (*pos == '[')
+ {
+ pos++;
+ parse_white();
+ item->cf.cls = CC_DYNAMIC;
+ byte *num = pos;
+ while (*pos && *pos != ']')
+ pos++;
+ if (!*pos)
+ die("Missing ']'");
+ *pos++ = 0;
+ if (!*num)
+ item->cf.number = CF_ANY_NUM;
+ else
+ {
+ int inum;
+ TRY(cf_parse_int(num, &inum));
+ if (!inum)
+ die("Invalid array length");
+ item->cf.number = inum;
+ }
+ parse_white();
+ }
+ if (*pos == '=')
+ {
+ pos++;
+ parse_white();
+ if (section->item.cf.cls == CC_LIST)
+ die("List items can not have default values");
+ if (item->cf.cls == CC_DYNAMIC)
+ die("Arrays can not have default values");
+ byte *def = pos, *d = def;
+ while (*pos != ';' && *pos != '}' && !Cspace(*pos))
+ {
+ if (*pos == '\'')
+ {
+ pos++;
+ while (*pos != '\'')
+ {
+ if (!*pos)
+ die("Unterminated string");
+ *d++ = *pos++;
+ }
+ pos++;
+ }
+ else if (*pos == '"')
+ {
+ pos++;
+ byte *start = d;
+ uns esc = 0;
+ while (*pos != '"' || esc)
+ {
+ if (!*pos)
+ die("Unterminated string");
+ if (*pos == '\\')
+ esc ^= 1;
+ else
+ esc = 0;
+ *d++ = *pos++;
+ }
+ pos++;
+ *d = 0;
+ d = str_unesc(start, start);
+ }
+ else
+ *d++ = *pos++;
+ }
+ uns len = d - def;
+ byte *buf = mp_alloc(pool, len + 1);
+ memcpy(buf, def, len);
+ buf[len] = 0;
+ switch (item->cf.type)
+ {
+ case CT_STRING:
+ item->value.v_ptr = buf;
+ break;
+ case CT_INT:
+ TRY(cf_parse_int(buf, &item->value.v_int));
+ break;
+ case CT_U64:
+ TRY(cf_parse_u64(buf, &item->value.v_u64));
+ break;
+ case CT_DOUBLE:
+ TRY(cf_parse_double(buf, &item->value.v_double));
+ break;
+ default:
+ ASSERT(0);
+ }
+ }
+ }
+ if (section->item.cf.cls == CC_LIST)
+ {
+ item->cf.ptr = (void *)(uintptr_t)section->size;
+ section->size += sizeof(union value);
+ }
+ else
+ item->cf.ptr = &item->value;
+ clist_add_tail(§ion->list, &item->node);
+ section->count++;
+ }
+#undef TRY
+}
+
+static void
+parse_outer(void)
+{
+ for (uns sep = 0; ; sep = 1)
+ {
+ parse_white();
+ if (!*pos)
+ break;
+ if (sep)
+ parse_char(';');
+ parse_white();
+ struct section *sec = mp_alloc_zero(pool, sizeof(*sec));
+ if (*pos == '!')
+ {
+ pos++;
+ sec->item.flags |= FLAG_NO_UNKNOWN;
+ }
+ sec->item.cf.name = parse_name();
+ parse_white();
+ parse_char('{');
+ clist_add_tail(§ions, &sec->item.node);
+ clist_init(&sec->list);
+ parse_section(sec);
+ parse_char('}');
+ }
+}
+
+static struct cf_section *
+generate_section(struct section *section)
+{
+ struct cf_section *sec = mp_alloc_zero(pool, sizeof(*sec));
+ if (section->item.cf.cls == CC_LIST)
+ sec->size = section->size;
+ struct cf_item *c = sec->cfg = mp_alloc_zero(pool, sizeof(struct cf_item) * (section->count + 1));
+ CLIST_FOR_EACH(struct item *, item, section->list)
+ {
+ *c = item->cf;
+ if (c->cls == CC_LIST)
+ c->u.sec = generate_section((struct section *)item);
+ c++;
+ }
+ c->cls = CC_END;
+ return sec;
+}
+
+static bb_t path;
+
+static void
+dump_value(uns array, struct item *item, void *v)
+{
+ byte buf[128], *value = buf;
+ if (!array)
+ printf("CF_%s_%s='", path.ptr, item->cf.name);
+ else
+ printf("CF_%s_%s[%u]='", path.ptr, item->cf.name, ++item->index);
+ switch (item->cf.type)
+ {
+ case CT_INT:
+ sprintf(buf, "%d", *(int *)v);
+ break;
+ case CT_U64:
+ sprintf(buf, "%llu", (long long) *(u64 *)v);
+ break;
+ case CT_DOUBLE:
+ sprintf(buf, "%g", *(double *)v);
+ break;
+ case CT_STRING:
+ if (*(byte **)v)
+ value = *(byte **)v;
+ else
+ *value = 0;
+ break;
+ default:
+ ASSERT(0);
+ }
+ while (*value) {
+ if (*value == '\'')
+ printf("'\\''");
+ else
+ putchar(*value);
+ value++;
+ }
+ printf("'\n");
+}
+
+static void
+dump_item(struct item *item, void *ptr, uns path_len)
+{
+ if (item->flags & FLAG_HIDE)
+ return;
+ byte *val = (byte *)((uintptr_t)ptr + (uintptr_t)item->cf.ptr);
+ if (item->cf.cls == CC_LIST)
+ {
+ uns len = strlen(item->cf.name);
+ bb_grow(&path, path_len + len + 1);
+ path.ptr[path_len] = '_';
+ memcpy(path.ptr + path_len + 1, item->cf.name, len);
+ CLIST_FOR_EACH(cnode *, ptr2, *(clist *)val)
+ CLIST_FOR_EACH(struct item *, item2, ((struct section *)item)->list)
+ dump_item(item2, ptr2, path_len + len + 1);
+ }
+ else
+ {
+ bb_grow(&path, path_len + 1)[path_len] = 0;
+ if (item->cf.cls == CC_STATIC)
+ dump_value(!!ptr, item, val);
+ else
+ {
+ val = *(void **)val;
+ uns len = DARY_LEN(val);
+ uns size = cf_type_size(item->cf.type, NULL);
+ for (uns i = 0; i < len; i++, val += size)
+ dump_value(1, item, val);
+ }
+ }
+}
+
+int main(int argc, char **argv)
+{
+ log_init("ucw-config");
+ if (argc < 2)
+ help();
+ pos = argv[argc - 1];
+ argv[argc - 1] = NULL;
+
+ pool = mp_new(0x1000);
+ clist_init(§ions);
+ parse_outer();
+ CLIST_FOR_EACH(struct section *, sec, sections)
+ cf_declare_section(sec->item.cf.name, generate_section(sec), !(sec->item.flags & FLAG_NO_UNKNOWN));
+
+ if (cf_getopt(argc - 1, argv, CF_SHORT_OPTS, CF_NO_LONG_OPTS, NULL) != -1)
+ help();
+
+ bb_init(&path);
+ CLIST_FOR_EACH(struct section *, section, sections)
+ {
+ uns len = strlen(section->item.cf.name);
+ memcpy(bb_grow(&path, len), section->item.cf.name, len);
+ CLIST_FOR_EACH(struct item *, item, section->list)
+ dump_item(item, NULL, len);
+ }
+ bb_done(&path);
+
+ return 0;
+}
+
--- /dev/null
+/*
+ * UCW Library Utilities -- A Simple Logger for use in shell scripts
+ *
+ * (c) 2001--2009 Martin Mares <mj@ucw.cz>
+ *
+ * This software may be freely distributed and used according to the terms
+ * of the GNU Lesser General Public License.
+ */
+
+#include <ucw/lib.h>
+#include <ucw/log.h>
+
+#include <stdio.h>
+#include <string.h>
+
+int
+main(int argc, char **argv)
+{
+ byte buf[1024], *c;
+
+ log_init("ucw-logger");
+ if (argc < 3 || argc > 4 || strlen(argv[2]) != 1)
+ die("Usage: ucw-logger [<logname>:]<progname> <level> [<text>]");
+ if (c = strchr(argv[1], ':'))
+ {
+ *c++ = 0;
+ log_init(c);
+ log_file(argv[1]);
+ }
+ else
+ log_init(argv[1]);
+
+ uns level = 0;
+ while (level < L_MAX && LS_LEVEL_LETTER(level) != argv[2][0])
+ level++;
+ if (level >= L_MAX)
+ die("Unknown logging level `%s'", argv[2]);
+
+ if (argc > 3)
+ msg(level, argv[3]);
+ else
+ while (fgets(buf, sizeof(buf), stdin))
+ {
+ c = strchr(buf, '\n');
+ if (c)
+ *c = 0;
+ msg(level, buf);
+ }
+ return 0;
+}
--- /dev/null
+/*
+ * UCW Library Utilities -- A Simple Logger for use in shell scripts
+ *
+ * (c) 2001--2009 Martin Mares <mj@ucw.cz>
+ * (c) 2011 Tomas Ebenlendr <ebik@ucw.cz>
+ *
+ * This software may be freely distributed and used according to the terms
+ * of the GNU Lesser General Public License.
+ */
+
+#undef LOCAL_DEBUG
+
+#include <ucw/lib.h>
+#include <ucw/log.h>
+#include <ucw/mainloop.h>
+#include <ucw/clists.h>
+#include <ucw/getopt.h>
+#include <ucw/conf.h>
+#include <ucw/process.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+
+static uns max_line = 1024;
+static int launch_finish_messages = 1;
+static int nonzero_status_message = 1;
+
+static struct cf_section cfsec_logoutput = {
+ CF_ITEMS {
+ CF_UNS("LineMax", &max_line),
+ CF_END
+ }
+};
+
+static clist filedescriptors;
+
+struct fds {
+ cnode node;
+ int pipe[2];
+ int fdnum;
+ uns level;
+ int long_continue;
+ struct main_rec_io rio;
+};
+
+static void
+close_fd(struct fds *fd)
+{
+ rec_io_del(&fd->rio);
+ close(fd->fdnum);
+ clist_remove(&fd->node);
+ if (clist_empty(&filedescriptors))
+ main_shut_down();
+}
+
+
+static void
+do_msg (struct fds *fd, char *l_msg, int long_continue)
+{
+ msg(fd->level, "%s%s", (fd->long_continue ? "... " : ""), l_msg);
+ fd->long_continue = long_continue;
+}
+
+static uns
+handle_read(struct main_rec_io *r)
+{
+ char buf[max_line + 5];
+ byte *eol = memchr((char *)r->read_rec_start + r->read_prev_avail, '\n', r->read_avail - r->read_prev_avail);
+ if (eol == NULL) {
+ if (r->read_avail >= max_line) {
+ memcpy(buf, r->read_rec_start, max_line);
+ memcpy(buf + max_line, " ...", 5);
+ do_msg(r->data, buf, 1);
+ return max_line;
+ } else
+ return 0;
+ }
+ *eol = 0;
+ byte *b = r->read_rec_start;
+ while ((uns)(eol - b) > max_line) {
+ char cc = b[max_line];
+ b[max_line]=0;
+ do_msg(r->data, b, 1);
+ b[max_line]=cc;
+ b+=max_line;
+ }
+ do_msg(r->data, (char *)b, 0);
+ return eol - r->read_rec_start + 1;
+}
+
+static int
+handle_notify(struct main_rec_io *r, int status)
+{
+ struct fds *fd = r->data;
+ switch (status) {
+ case RIO_ERR_READ:
+ case RIO_EVENT_EOF:
+ if (r->read_avail) {
+ char buf[max_line + 10];
+ memcpy(buf, r->read_rec_start, r->read_avail);
+ memcpy(buf + r->read_avail, " [no eol]", 10);
+ do_msg(r->data, buf, 0);
+ } else if (fd->long_continue) {
+ do_msg(r->data, "[no eol]", 0);
+ }
+ close_fd(fd);
+ return HOOK_DONE;
+ default:
+ ASSERT(0);
+ }
+ return HOOK_IDLE;
+}
+
+
+static void
+add_level_fd(int fdnum, int level)
+{
+ struct fds *fd = xmalloc_zero(sizeof(*fd));
+ fd->level = level;
+ fd->pipe[0] = -1;
+ fd->pipe[1] = -1;
+ fd->fdnum = fdnum;
+ fd->rio.read_handler = handle_read;
+ fd->rio.data = fd;
+ fd->rio.notify_handler = handle_notify;
+ fd->long_continue = 0;
+ clist_add_tail(&filedescriptors, &fd->node);
+}
+
+static int
+xdup(int fd)
+{
+ int rfd = dup(fd);
+ if (rfd == -1)
+ die("Cannot dup(): %m");
+ DBG(" Dup(%i) -> %i", fd, rfd);
+ return rfd;
+}
+
+static int
+xdup2(int fd1, int fd2)
+{
+ int rfd = dup2(fd1, fd2);
+ if (rfd == -1)
+ die("Cannot dup2(): %m");
+ DBG(" Dup2(%i, %i) -> %i", fd1, fd2, rfd);
+ return rfd;
+}
+
+static void
+xdupavoid(int *fd1, int fd2)
+{
+ DBG("Dupavoid: !%i -> %i", fd2, *fd1);
+ int ofd = *fd1;
+ if (ofd == fd2) {
+ *fd1 = xdup(ofd);
+ DBG(" Close: %i", ofd);
+ close(ofd);
+ }
+}
+
+static void
+xdupto(int *fd1, int fd2)
+{
+ DBG("Dupto: %i -> %i", *fd1, fd2);
+ if (*fd1 == fd2)
+ return;
+ DBG(" Close: %i", fd2);
+ close(fd2);
+ xdup2(*fd1, fd2);
+ DBG(" Close: %i", *fd1);
+ close(*fd1);
+ *fd1 = fd2;
+}
+
+static void
+set_cloexec_flag(int fd, int value)
+{
+ int flags = fcntl(fd, F_GETFD, 0);
+ if (flags < 0)
+ die("fcntl(..., F_GETFD, ...) : %m");
+ flags = (value) ? flags | FD_CLOEXEC : flags & ~FD_CLOEXEC;
+ if (fcntl(fd, F_SETFD, flags) < 0)
+ die("fcntl(..., F_SETFD, ...) : %m");
+}
+
+/* The "+" stands for end at first argument (i.e. do not parse options after
+ first argument.) */
+#define MY_SHORT_OPTS "+" CF_SHORT_OPTS "f:n:l:ih"
+const struct option my_long_opts[] = {
+ CF_LONG_OPTS
+ { "help", 0, 0, 'h'},
+ { "input", 0, 0, 'i'},
+ { "logfile", 1, 0, 'f'},
+ { "logname", 1, 0, 'n'},
+ { "descriptor", 1, 0, 'l'},
+ { "nv", 0, 0, 'q'},
+ { "nonverbose", 0, 0, 'q'},
+ { "non-verbose", 0, 0, 'q'},
+ { "non_verbose", 0, 0, 'q'},
+ { "silent", 0, 0, 's'},
+ { NULL, 0, 0, 0}
+};
+
+#undef CF_USAGE_TAB
+#define CF_USAGE_TAB "\t "
+static char usage[] =
+ "Usage:\n"
+ "ucw-logoutput -h|--help\t\t This help.\n"
+ "ucw-logoutput <options> -i|--input Read file descriptors and log them.\n"
+ "\t\t\t\t default: stdin at level I.\n"
+ "ucw-logoutput <opts> [--] <cmd> [arguments for cmd ...]\n"
+ "\t\t\t\t Open file descriptors for writing for command <cmd> and log them.\n"
+ "\t\t\t\t default: stdout:I, stderr:W.\n\n"
+ "Options:\n"
+ CF_USAGE
+ "-n, --logname <name>\t\t Use <name> as program name in logs.\n"
+ "-l, --descriptor <fdnum>:<level> Open file descriptor <fdnum> and log it at level <level> (replaces defaults).\n"
+ "-f, --logfile <logfile>\t\t Log to file <logfile>.\n"
+ "-q, --nv, --nonverbose\t\t Suppress launching and successful finish messages.\n"
+ "-s, --silent\t\t\t Suppress launching message and all finish messages.\n"
+ "\t\t\t\t (i.e., no warning if it terminates with a nonzero exit code or by a signal)\n";
+
+int
+main(int argc, char **argv)
+{
+ int register_default = 1;
+ int loginput = 0;
+ char *logfile = NULL;
+ char *logname = NULL;
+ struct fds *stderrfd = NULL;
+ int help = 0;
+
+ log_init("ucw-logoutput");
+ clist_init(&filedescriptors);
+ cf_declare_section("LogOutput", &cfsec_logoutput, 0);
+
+ while (1) {
+ int opt = cf_getopt(argc, argv, MY_SHORT_OPTS, my_long_opts, NULL);
+ switch (opt) {
+ case -1:
+ goto opt_done;
+
+ case 'h':
+ help = 1;
+ break;
+
+ case 'i':
+ loginput = 1;
+ break;
+
+ case 'f':
+ logfile = optarg;
+ break;
+
+ case 'n':
+ logname = optarg;
+ break;
+
+ case 'l':
+ {
+ char *c = optarg;
+
+ register_default = 0;
+ int fdnum = 0;
+ int parseerror = 0;
+ if ( (c[0]<'0') || (c[0] > '9') )
+ parseerror = 1;
+ while ( (!parseerror) && (c[0] >= '0') && (c[0] <= '9') )
+ { fdnum = fdnum*10 + c[0] - '0'; c++; }
+ if ( (!parseerror) && (c[0] != ':') )
+ parseerror = 1;
+ c++;
+ if ( (!parseerror) && (c[0] == 0) )
+ parseerror = 1;
+ if ( (!parseerror) && (c[1] != 0) )
+ parseerror = 1;
+ if (parseerror) die("Bad argument `%s' to -l, expects number:letter.", optarg);
+
+ uns level = 0;
+ while (level < L_MAX && LS_LEVEL_LETTER(level) != c[0])
+ level++;
+ if (level >= L_MAX)
+ die("Unknown logging level `%s'", c);
+
+ add_level_fd(fdnum, level);
+ }
+ break;
+
+ case 's':
+ nonzero_status_message = 0;
+ /* fallthrough */
+ case 'q':
+ launch_finish_messages = 0;
+ break;
+
+ default:
+ optind--;
+ goto opt_done;
+ }
+ }
+opt_done:
+
+ if (!help) {
+ if (loginput && (optind < argc))
+ die("No cmd is allowed for -i. Use -h for help.");
+
+ if ((!loginput) && (optind >= argc)) {
+ msg(L_FATAL, "Either command or --input expected.");
+ help = 2;
+ }
+ }
+ if (help) {
+ write(2, usage, sizeof(usage));
+ return (help == 1) ? 0 : 1;
+ }
+
+ if (register_default) {
+ if (loginput) {
+ add_level_fd(0, L_INFO);
+ } else {
+ add_level_fd(1, L_INFO);
+ add_level_fd(2, L_WARN);
+ }
+ }
+
+ if (loginput) {
+ /* Just check, that we don't want open stderr for reading. */
+ CLIST_FOR_EACH(struct fds *, fd, filedescriptors) {
+ if (fd->fdnum == 2)
+ die("Stderr is reserved for output");
+ }
+ } else {
+ /* Open all filedescriptors and their duplicates. */
+ CLIST_FOR_EACH(struct fds *, fd, filedescriptors) {
+ CLIST_FOR_EACH(struct fds *, fdcheck, filedescriptors) {
+ /* We do a dummy check for collisions of filedescriptors. */
+ if (fdcheck == fd)
+ break;
+ if (fdcheck->fdnum == fd->fdnum) {
+ die("Duplicate filedescriptor %i", fd->fdnum);
+ }
+ xdupavoid(fdcheck->pipe + 0, fd->fdnum);
+ xdupavoid(fdcheck->pipe + 1, fd->fdnum);
+ }
+ if (pipe(fd->pipe) == -1)
+ die("Cannot create pipe: %m");
+ DBG("Pipe [%i, %i] for %i", fd->pipe[0], fd->pipe[1], fd->fdnum);
+ xdupavoid(fd->pipe + 0, fd->fdnum);
+
+ if (fd->fdnum == 2) {
+ stderrfd = fd; //We need to redirect stderr later.
+ } else {
+ xdupto(fd->pipe + 1, fd->fdnum);
+ }
+ DBG("---> [%i, %i] for %i", fd->pipe[0], fd->pipe[1], fd->fdnum);
+ set_cloexec_flag(fd->pipe[0], 1);
+ set_cloexec_flag(fd->pipe[1], 0);
+ }
+ }
+
+ /* Initialize main loop. */
+ main_init();
+
+ CLIST_FOR_EACH(struct fds *, fd, filedescriptors) {
+ /* Our pipe is created, let fd->fdnum be the reading end. */
+ if (!loginput)
+ fd->fdnum = fd->pipe[0];
+ fd->rio.read_rec_max = max_line + 1;
+ rec_io_add(&fd->rio, fd->fdnum);
+ }
+
+ int pid = -1;
+ if (!loginput) {
+ /* Launch the child and close filedescriptors. */
+ pid = fork();
+ if (pid == -1)
+ die("Cannot fork: %m");
+ if (pid == 0 ) {
+ /* Child */
+
+ /* Move stderr where it should be. */
+ if (stderrfd)
+ xdupto(stderrfd->pipe + 1, 2);
+
+ execvp(argv[optind], argv + optind);
+ if (stderrfd) {
+ /* We translate stderr, just print. */
+ perror("Cannot exec child");
+ return 127;
+ }
+ /* No stderr translation: use logging function. */
+ die("Cannot exec child: %m");
+ }
+
+ /* Close writing filedescriptors. */
+ CLIST_FOR_EACH(struct fds *, fd, filedescriptors) {
+ close(fd->pipe[1]);
+ }
+ }
+
+ /* Open logfile or stderr. */
+ if (logfile) {
+ log_file(logfile);
+ close(2);
+ }
+
+ if (!loginput) {
+ /* Inform about launching of the command. */
+ int buflen = 0;
+ for (int i = optind; i < argc; i++) buflen += strlen(argv[i]) + 1;
+ char *buf = xmalloc(buflen);
+ char *buf2 = buf;
+ for (int i = optind; i < argc; i++) {
+ strcpy(buf2, argv[i]);
+ buf2 += strlen(argv[i]);
+ buf2[0] = ' ';
+ buf2++;
+ }
+ buf2[-1] = 0;
+
+ if (launch_finish_messages)
+ msg(L_INFO, "Launching command: %s", buf);
+ }
+
+ /* Set logname. */
+ if (logname)
+ log_init(logname);
+ else if (!loginput)
+ log_init(argv[optind]);
+
+ /* Start reading from pipes. */
+ CLIST_FOR_EACH(struct fds *, fd, filedescriptors)
+ rec_io_start_read(&fd->rio);
+ main_loop();
+
+ if (!loginput) {
+ /* Unset logname. */
+ log_init("ucw-logoutput");
+
+ /* Wait for status of the child and inform about finish. */
+ int status;
+ char buf[256];
+
+ while (waitpid(pid, &status, 0) == -1) {
+ if (errno != EINTR)
+ die("Cannot wait for child: %m");
+ }
+
+ if (format_exit_status(buf, status)) {
+ if (nonzero_status_message)
+ msg(L_WARN, "Child %s", buf);
+ return WIFEXITED(status) ? WEXITSTATUS(status) : 127;
+ } else {
+ if (launch_finish_messages)
+ msg(L_INFO, "Child terminated successfully.");
+ return 0;
+ }
+ }
+
+ return 0;
+}
# Makefile for the UCW utilities (c) 2008 Michal Vaner <vorner@ucw.cz>
-UCW_UTILS=$(addprefix $(o)/ucw/utils/,basecode daemon-control rotate-log urltool)
+UCW_UTILS=$(addprefix $(o)/ucw/utils/ucw-,basecode daemon-control rotate-log urltool)
PROGS+=$(UCW_UTILS)
DIRS+=ucw/utils
ifdef CONFIG_UCW_OBSOLETE_DAEMON_HELPER
-PROGS+=$(o)/ucw/utils/daemon-helper
+PROGS+=$(o)/ucw/utils/ucw-daemon-helper
endif
-$(o)/ucw/utils/basecode: $(o)/ucw/utils/basecode.o $(LIBUCW)
-$(o)/ucw/utils/daemon-helper: $(o)/ucw/utils/daemon-helper.o $(LIBUCW)
-$(o)/ucw/utils/daemon-control: $(o)/ucw/utils/daemon-control.o $(LIBUCW)
-$(o)/ucw/utils/urltool: $(o)/ucw/utils/urltool.o $(LIBUCW)
+$(o)/ucw/utils/ucw-basecode: $(o)/ucw/utils/ucw-basecode.o $(LIBUCW)
+$(o)/ucw/utils/ucw-daemon-helper: $(o)/ucw/utils/ucw-daemon-helper.o $(LIBUCW)
+$(o)/ucw/utils/ucw-daemon-control: $(o)/ucw/utils/ucw-daemon-control.o $(LIBUCW)
+$(o)/ucw/utils/ucw-urltool: $(o)/ucw/utils/ucw-urltool.o $(LIBUCW)
TESTS+=$(o)/ucw/utils/basecode.test
-$(o)/ucw/utils/basecode.test: $(o)/ucw/utils/basecode
+$(o)/ucw/utils/basecode.test: $(o)/ucw/utils/ucw-basecode
INSTALL_TARGETS+=install-ucw-utils
install-ucw-utils:
+++ /dev/null
-/*
- * UCW Library -- Line utility for encoding and decoding base64 & base224
- *
- * (c) 2008, Michal Vaner <vorner@ucw.cz>
- *
- * This software may be freely distributed and used according to the terms
- * of the GNU Lesser General Public License.
- */
-
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <ucw/lib.h>
-#include <ucw/base64.h>
-#include <ucw/base224.h>
-#include <ucw/fastbuf.h>
-#include <ucw/getopt.h>
-
-static struct option opts[] = {
- { "encode64", 0, 0, 'e' },
- { "decode64", 0, 0, 'd' },
- { "encode224", 0, 0, 'E' },
- { "decode224", 0, 0, 'D' },
- { "prefix", 1, 0, 'p' },
- { "blocks", 1, 0, 'b' },
- { 0, 0, 0, 0 }
-};
-
-static const struct {
- uns (*function)(byte *, const byte *, uns);
- uns in_block, out_block, num_blocks;
- uns add_prefix;
-} actions[] = {
- {
- base64_encode,
- BASE64_IN_CHUNK, BASE64_OUT_CHUNK, 20,
- 1
- },
- {
- base64_decode,
- BASE64_OUT_CHUNK, BASE64_IN_CHUNK, 20,
- 0
- },
- {
- base224_encode,
- BASE224_IN_CHUNK, BASE64_OUT_CHUNK, 6,
- 1
- },
- {
- base224_decode,
- BASE224_OUT_CHUNK, BASE224_IN_CHUNK, 6,
- 0
- }
-};
-
-int main(int argc, char **argv)
-{
- // Choose mode
- int mode = -1;
- char *prefix = NULL;
- uns blocks = 0;
- int opt;
- while ((opt = getopt_long(argc, argv, "edEDp:b:", opts, NULL)) >= 0)
- switch (opt)
- {
- case 'e': mode = 0; break;
- case 'd': mode = 1; break;
- case 'E': mode = 2; break;
- case 'D': mode = 3; break;
- case 'p': prefix = optarg; break;
- case 'b':
- {
- char *end;
- blocks = strtol(optarg, &end, 0);
- if ((blocks > 0) && !*end)
- break;
- }
- default: goto usage;
- }
-
- if (mode == -1)
- {
- usage:
- fprintf(stderr, "basecode mode [--prefix=prefix] [--blocks=number_of_blocks]\nMode is one of:\n\t--encode64 (-e)\n\t--decode64 (-d)\n\t--encode224 (-E)\n\t--decode224 (-D)\n");
- return 1;
- }
- if (!blocks)
- blocks = actions[mode].num_blocks;
-
- // Prepare buffers
- struct fastbuf *in = bfdopen_shared(0, 4096);
- struct fastbuf *out = bfdopen_shared(1, 4096);
- int has_offset = !actions[mode].add_prefix && prefix;
- uns offset = has_offset ? strlen(prefix) : 0;
- uns read_size = actions[mode].in_block * blocks + offset + has_offset;
- uns write_size = actions[mode].out_block * blocks;
- byte in_buff[read_size], out_buff[write_size];
- uns isize;
-
- // Recode it
- while (isize = bread(in, in_buff, read_size))
- {
- if (prefix)
- {
- if (actions[mode].add_prefix)
- bputs(out, prefix);
- else
- if ((isize < offset) || (in_buff[isize-1] != '\n')
- || (strncmp(prefix, in_buff, offset)))
- die("Invalid line syntax");
- }
- uns osize = actions[mode].function(out_buff, in_buff + offset, isize - offset - has_offset);
- bwrite(out, out_buff, osize);
- if (actions[mode].add_prefix && prefix)
- bputc(out, '\n');
- }
-
- bclose(in);
- bclose(out);
- return 0;
-}
# Tests for base64 and base224 modules
Name: Base64 encode
-Run: ../obj/ucw/utils/basecode -e
+Run: ../obj/ucw/utils/ucw-basecode -e
In: Here are some test data
Out: SGVyZSBhcmUgc29tZSB0ZXN0IGRhdGEK
Name: Base64 decode
-Run: ../obj/ucw/utils/basecode -d
+Run: ../obj/ucw/utils/ucw-basecode -d
In: SGVyZSBhcmUgc29tZSB0ZXN0IGRhdGEK
Out: Here are some test data
Name: Base224 encode & decode
-Run: ../obj/ucw/utils/basecode -E | ../obj/ucw/utils/basecode -D
+Run: ../obj/ucw/utils/ucw-basecode -E | ../obj/ucw/utils/ucw-basecode -D
In: Some more test data for 224 encoding
Out: Some more test data for 224 encoding
+++ /dev/null
-/*
- * A Simple Utility for Controlling Daemons
- *
- * (c) 2012 Martin Mares <mj@ucw.cz>
- *
- * For more information, see ucw/doc/daemon.txt.
- *
- * Return codes:
- * 100 already done
- * 101 not running
- * 102 error
- */
-
-#include <ucw/lib.h>
-#include <ucw/daemon.h>
-#include <ucw/signames.h>
-#include <ucw/string.h>
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <signal.h>
-#include <getopt.h>
-
-static int action;
-
-static struct option options[] = {
- { "pid-file", required_argument, NULL, 'p' },
- { "guard-file", required_argument, NULL, 'g' },
- { "signal", required_argument, NULL, 's' },
- { "start", no_argument, &action, DAEMON_CONTROL_START },
- { "stop", no_argument, &action, DAEMON_CONTROL_STOP },
- { "check", no_argument, &action, DAEMON_CONTROL_CHECK },
- { "reload", no_argument, &action, DAEMON_CONTROL_SIGNAL },
- { NULL, no_argument, NULL, 0 }
-};
-
-static void NONRET
-usage(void)
-{
- fputs("\n\
-Usage: daemon-control --start <options> -- <daemon> <args>\n\
- or: daemon-control --stop <options>\n\
- or: daemon-control --reload <options>\n\
- or: daemon-control --check <options>\n\
-\n\
-Options:\n\
---pid-file <name> Name of PID file for this daemon (mandatory)\n\
---guard-file <name> Name of guard file (default: derived from --pid-file)\n\
---signal <sig> Send a signal of a given name or number\n\
- Default: SIGTERM for --stop, SIGHUP for --reload\n\
-\n\
-Exit codes:\n\
-0 Successfully completed\n\
-1 Invalid arguments\n\
-100 The action was null (e.g., --stop on a stopped daemon)\n\
-101 The daemon was not running (on --reload or --check)\n\
-102 The action has failed (error message was printed to stderr)\n\
-103 The daemon was in an undefined state (e.g., stale PID file)\n\
-", stderr);
- exit(1);
-}
-
-int
-main(int argc, char **argv)
-{
- int c, sig;
- struct daemon_control_params dc = { };
-
- while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0)
- switch (c)
- {
- case 0:
- break;
- case 'p':
- dc.pid_file = optarg;
- break;
- case 'g':
- dc.guard_file = optarg;
- break;
- case 's':
- sig = sig_name_to_number(optarg);
- if (sig < 0)
- {
- fprintf(stderr, "%s: Unknown signal %s\n", argv[0], optarg);
- return 1;
- }
- dc.signal = sig;
- break;
- default:
- usage();
- }
- if (!dc.pid_file || !action)
- usage();
- dc.action = action;
-
- if (action == DAEMON_CONTROL_START)
- {
- if (optind >= argc)
- usage();
- dc.argv = argv + optind;
- }
- else if (optind < argc)
- usage();
-
- if (!dc.guard_file)
- {
- if (!str_has_suffix(dc.pid_file, ".pid"))
- {
- fprintf(stderr, "%s: For automatic choice of --guard-file, the --pid-file must end with `.pid'\n", argv[0]);
- return 1;
- }
- int len = strlen(dc.pid_file);
- char *buf = xmalloc(len + 2);
- sprintf(buf, "%.*s.lock", len-4, dc.pid_file);
- dc.guard_file = buf;
- }
-
- int err = daemon_control(&dc);
- if (err == DAEMON_STATUS_ERROR)
- fprintf(stderr, "%s: %s\n", argv[0], dc.error_msg);
- return err;
-}
+++ /dev/null
-/*
- * A Simple Wrapper for Starting and Stopping of Daemons
- *
- * (c) 2003 Martin Mares <mj@ucw.cz>
- *
- * 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
- *
- * NOTE: This utility is obsolete and has been replaced by daemon-control.
- * You need to enable CONFIG_UCW_OBSOLETE_DAEMON_HELPER to compile it.
- */
-
-#include <ucw/lib.h>
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <signal.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <getopt.h>
-#include <errno.h>
-#include <alloca.h>
-
-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 <options> -- <daemon> <args>\n\
- or: daemon-helper --stop <options>\n\
- or: daemon-helper --force-stop <options>\n\
- or: daemon-helper --reload <options>\n\
- or: daemon-helper --check <options>\n\
-\n\
-Options:\n\
---pid-file <name> Name of PID file for this daemon (mandatory)\n\
---status-file <name> 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();
- }
-}
+++ /dev/null
-#!/usr/bin/perl
-
-# Rotate Sherlock logs
-# (c) 2001--2002 Martin Mares <mj@ucw.cz>
-
-use File::stat;
-
-@ARGV >= 3 or die "Usage: rotate-log <days-to-compress> <date-to-delete> <logs...>";
-
-$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: $!";
- }
-}
--- /dev/null
+/*
+ * UCW Library -- Line utility for encoding and decoding base64 & base224
+ *
+ * (c) 2008, Michal Vaner <vorner@ucw.cz>
+ *
+ * This software may be freely distributed and used according to the terms
+ * of the GNU Lesser General Public License.
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <ucw/lib.h>
+#include <ucw/base64.h>
+#include <ucw/base224.h>
+#include <ucw/fastbuf.h>
+#include <ucw/getopt.h>
+
+static struct option opts[] = {
+ { "encode64", 0, 0, 'e' },
+ { "decode64", 0, 0, 'd' },
+ { "encode224", 0, 0, 'E' },
+ { "decode224", 0, 0, 'D' },
+ { "prefix", 1, 0, 'p' },
+ { "blocks", 1, 0, 'b' },
+ { 0, 0, 0, 0 }
+};
+
+static const struct {
+ uns (*function)(byte *, const byte *, uns);
+ uns in_block, out_block, num_blocks;
+ uns add_prefix;
+} actions[] = {
+ {
+ base64_encode,
+ BASE64_IN_CHUNK, BASE64_OUT_CHUNK, 20,
+ 1
+ },
+ {
+ base64_decode,
+ BASE64_OUT_CHUNK, BASE64_IN_CHUNK, 20,
+ 0
+ },
+ {
+ base224_encode,
+ BASE224_IN_CHUNK, BASE64_OUT_CHUNK, 6,
+ 1
+ },
+ {
+ base224_decode,
+ BASE224_OUT_CHUNK, BASE224_IN_CHUNK, 6,
+ 0
+ }
+};
+
+int main(int argc, char **argv)
+{
+ // Choose mode
+ int mode = -1;
+ char *prefix = NULL;
+ uns blocks = 0;
+ int opt;
+ while ((opt = getopt_long(argc, argv, "edEDp:b:", opts, NULL)) >= 0)
+ switch (opt)
+ {
+ case 'e': mode = 0; break;
+ case 'd': mode = 1; break;
+ case 'E': mode = 2; break;
+ case 'D': mode = 3; break;
+ case 'p': prefix = optarg; break;
+ case 'b':
+ {
+ char *end;
+ blocks = strtol(optarg, &end, 0);
+ if ((blocks > 0) && !*end)
+ break;
+ }
+ default: goto usage;
+ }
+
+ if (mode == -1)
+ {
+ usage:
+ fprintf(stderr, "ucw-basecode mode [--prefix=prefix] [--blocks=number_of_blocks]\nMode is one of:\n\t--encode64 (-e)\n\t--decode64 (-d)\n\t--encode224 (-E)\n\t--decode224 (-D)\n");
+ return 1;
+ }
+ if (!blocks)
+ blocks = actions[mode].num_blocks;
+
+ // Prepare buffers
+ struct fastbuf *in = bfdopen_shared(0, 4096);
+ struct fastbuf *out = bfdopen_shared(1, 4096);
+ int has_offset = !actions[mode].add_prefix && prefix;
+ uns offset = has_offset ? strlen(prefix) : 0;
+ uns read_size = actions[mode].in_block * blocks + offset + has_offset;
+ uns write_size = actions[mode].out_block * blocks;
+ byte in_buff[read_size], out_buff[write_size];
+ uns isize;
+
+ // Recode it
+ while (isize = bread(in, in_buff, read_size))
+ {
+ if (prefix)
+ {
+ if (actions[mode].add_prefix)
+ bputs(out, prefix);
+ else
+ if ((isize < offset) || (in_buff[isize-1] != '\n')
+ || (strncmp(prefix, in_buff, offset)))
+ die("Invalid line syntax");
+ }
+ uns osize = actions[mode].function(out_buff, in_buff + offset, isize - offset - has_offset);
+ bwrite(out, out_buff, osize);
+ if (actions[mode].add_prefix && prefix)
+ bputc(out, '\n');
+ }
+
+ bclose(in);
+ bclose(out);
+ return 0;
+}
--- /dev/null
+/*
+ * A Simple Utility for Controlling Daemons
+ *
+ * (c) 2012 Martin Mares <mj@ucw.cz>
+ *
+ * For more information, see ucw/doc/daemon.txt.
+ *
+ * Return codes:
+ * 100 already done
+ * 101 not running
+ * 102 error
+ */
+
+#include <ucw/lib.h>
+#include <ucw/daemon.h>
+#include <ucw/signames.h>
+#include <ucw/string.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <getopt.h>
+
+static int action;
+
+static struct option options[] = {
+ { "pid-file", required_argument, NULL, 'p' },
+ { "guard-file", required_argument, NULL, 'g' },
+ { "signal", required_argument, NULL, 's' },
+ { "start", no_argument, &action, DAEMON_CONTROL_START },
+ { "stop", no_argument, &action, DAEMON_CONTROL_STOP },
+ { "check", no_argument, &action, DAEMON_CONTROL_CHECK },
+ { "reload", no_argument, &action, DAEMON_CONTROL_SIGNAL },
+ { NULL, no_argument, NULL, 0 }
+};
+
+static void NONRET
+usage(void)
+{
+ fputs("\n\
+Usage: ucw-daemon-control --start <options> -- <daemon> <args>\n\
+ or: ucw-daemon-control --stop <options>\n\
+ or: ucw-daemon-control --reload <options>\n\
+ or: ucw-daemon-control --check <options>\n\
+\n\
+Options:\n\
+--pid-file <name> Name of PID file for this daemon (mandatory)\n\
+--guard-file <name> Name of guard file (default: derived from --pid-file)\n\
+--signal <sig> Send a signal of a given name or number\n\
+ Default: SIGTERM for --stop, SIGHUP for --reload\n\
+\n\
+Exit codes:\n\
+0 Successfully completed\n\
+1 Invalid arguments\n\
+100 The action was null (e.g., --stop on a stopped daemon)\n\
+101 The daemon was not running (on --reload or --check)\n\
+102 The action has failed (error message was printed to stderr)\n\
+103 The daemon was in an undefined state (e.g., stale PID file)\n\
+", stderr);
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ int c, sig;
+ struct daemon_control_params dc = { };
+
+ while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0)
+ switch (c)
+ {
+ case 0:
+ break;
+ case 'p':
+ dc.pid_file = optarg;
+ break;
+ case 'g':
+ dc.guard_file = optarg;
+ break;
+ case 's':
+ sig = sig_name_to_number(optarg);
+ if (sig < 0)
+ {
+ fprintf(stderr, "%s: Unknown signal %s\n", argv[0], optarg);
+ return 1;
+ }
+ dc.signal = sig;
+ break;
+ default:
+ usage();
+ }
+ if (!dc.pid_file || !action)
+ usage();
+ dc.action = action;
+
+ if (action == DAEMON_CONTROL_START)
+ {
+ if (optind >= argc)
+ usage();
+ dc.argv = argv + optind;
+ }
+ else if (optind < argc)
+ usage();
+
+ if (!dc.guard_file)
+ {
+ if (!str_has_suffix(dc.pid_file, ".pid"))
+ {
+ fprintf(stderr, "%s: For automatic choice of --guard-file, the --pid-file must end with `.pid'\n", argv[0]);
+ return 1;
+ }
+ int len = strlen(dc.pid_file);
+ char *buf = xmalloc(len + 2);
+ sprintf(buf, "%.*s.lock", len-4, dc.pid_file);
+ dc.guard_file = buf;
+ }
+
+ int err = daemon_control(&dc);
+ if (err == DAEMON_STATUS_ERROR)
+ fprintf(stderr, "%s: %s\n", argv[0], dc.error_msg);
+ return err;
+}
--- /dev/null
+/*
+ * A Simple Wrapper for Starting and Stopping of Daemons
+ *
+ * (c) 2003 Martin Mares <mj@ucw.cz>
+ *
+ * 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
+ *
+ * NOTE: This utility is obsolete and has been replaced by ucw-daemon-control.
+ * You need to enable CONFIG_UCW_OBSOLETE_DAEMON_HELPER to compile it.
+ */
+
+#include <ucw/lib.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <errno.h>
+#include <alloca.h>
+
+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: ucw-daemon-helper --start <options> -- <daemon> <args>\n\
+ or: ucw-daemon-helper --stop <options>\n\
+ or: ucw-daemon-helper --force-stop <options>\n\
+ or: ucw-daemon-helper --reload <options>\n\
+ or: ucw-daemon-helper --check <options>\n\
+\n\
+Options:\n\
+--pid-file <name> Name of PID file for this daemon (mandatory)\n\
+--status-file <name> 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();
+ }
+}
--- /dev/null
+#!/usr/bin/perl
+
+# Rotate Sherlock logs
+# (c) 2001--2002 Martin Mares <mj@ucw.cz>
+
+use File::stat;
+
+@ARGV >= 3 or die "Usage: ucw-rotate-log <days-to-compress> <date-to-delete> <logs...>";
+
+$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: $!";
+ }
+}
--- /dev/null
+/*
+ * Sherlock Utilities -- URL Handling Tool
+ *
+ * (c) 2004 Martin Mares <mj@ucw.cz>
+ */
+
+#include <ucw/lib.h>
+#include <ucw/getopt.h>
+#include <ucw/url.h>
+#include <ucw/fastbuf.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+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: ucw-urltool [<options>] <operations> [<URL's>]\n\
+\n\
+Options:\n"
+CF_USAGE "\
+-b, --base <URL>\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);
+}
+++ /dev/null
-/*
- * Sherlock Utilities -- URL Handling Tool
- *
- * (c) 2004 Martin Mares <mj@ucw.cz>
- */
-
-#include <ucw/lib.h>
-#include <ucw/getopt.h>
-#include <ucw/url.h>
-#include <ucw/fastbuf.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-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 [<options>] <operations> [<URL's>]\n\
-\n\
-Options:\n"
-CF_USAGE "\
--b, --base <URL>\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);
-}