Permalink
Browse files

Initial check-in of the Booth Cluster Ticket Manager.

Booth manages the ticket which authorizes one of the cluster sites located in
geographically dispersed distances to run certain resources. It is designed to
be an add-on of Pacemaker, which extends Pacemaker to support geographically
distributed clustering.

Booth includes an implementation of Paxos and Paxos Lease algorithm, which
guarantees the distributed consensus among different cluster sites. One
development goal of the Paxos implementation is to be flexible enough so that
it can also be used by some other projects which need Paxos.

Signed-off-by: Jiaju Zhang <jjzhang@suse.de>
  • Loading branch information...
0 parents commit 54dc3dc2be33cd977866ce63d8cc74934d21405d @jjzhang committed Aug 24, 2011
Showing with 5,248 additions and 0 deletions.
  1. +19 −0 Makefile
  2. +17 −0 README
  3. +89 −0 script/lsb/booth-arbitrator
  4. +180 −0 script/ocf/booth-site
  5. +54 −0 src/Makefile
  6. +81 −0 src/booth.h
  7. +270 −0 src/config.c
  8. +50 −0 src/config.h
  9. +568 −0 src/list.h
  10. +245 −0 src/log.c
  11. +32 −0 src/log.h
  12. +1,000 −0 src/main.c
  13. +141 −0 src/pacemaker.c
  14. +31 −0 src/pacemaker.h
  15. +718 −0 src/paxos.c
  16. +79 −0 src/paxos.h
  17. +441 −0 src/paxos_lease.c
  18. +66 −0 src/paxos_lease.h
  19. +340 −0 src/ticket.c
  20. +30 −0 src/ticket.h
  21. +106 −0 src/timer.c
  22. +41 −0 src/timer.h
  23. +592 −0 src/transport.c
  24. +58 −0 src/transport.h
19 Makefile
@@ -0,0 +1,19 @@
+INIT_DIR=/etc/init.d
+RSC_DIR=/usr/lib/ocf/resource.d/pacemaker
+BOOTH_SITE=./script/ocf/booth-site
+BOOTH_ARBITRATOR=./script/lsb/booth-arbitrator
+
+INSTALL=$(shell which install)
+
+all:
+ ${MAKE} -C ./src
+
+install:
+ ${MAKE} -C ./src install
+ mkdir -p $(DESTDIR)/$(RSC_DIR)
+ mkdir -p $(DESTDIR)/$(INIT_DIR)
+ $(INSTALL) -c -m 755 $(BOOTH_SITE) $(DESTDIR)/$(RSC_DIR)
+ $(INSTALL) -c -m 755 $(BOOTH_ARBITRATOR) $(DESTDIR)/$(INIT_DIR)
+
+clean:
+ ${MAKE} -C ./src clean
17 README
@@ -0,0 +1,17 @@
+The Booth Cluster Ticket Manager
+
+Booth manages the ticket which authorizes one of the cluster sites located in
+geographically dispersed distances to run certain resources. It is designed to
+be an add-on of Pacemaker, which extends Pacemaker to support geographically
+distributed clustering.
+
+Booth includes an implementation of Paxos and Paxos Lease algorithm, which
+guarantees the distributed consensus among different cluster sites. One
+development goal of the Paxos implementation is to be flexible enough so that
+it can also be used by some other projects which need Paxos.
+
+Now, the Booth is still in heavy development, review and comments are highly
+appreciated ;)
+
+Regards,
+Jiaju Zhang <jjzhang@suse.de>
89 script/lsb/booth-arbitrator
@@ -0,0 +1,89 @@
+#!/bin/bash
+#
+# BOOTH daemon init script for LSB-compliant Linux distributions.
+#
+# booth-arbitrator BOOTH arbitrator daemon
+#
+# chkconfig: - 20 20
+# processname: booth
+# pidfile: /var/run/booth.pid
+# description: Cluster Ticket Registry
+### BEGIN INIT INFO
+# Provides: booth
+# Required-Start: $network $syslog
+# Required-Stop: $network $syslog
+# Should-Start:
+# Should-Stop:
+# Default-Start: 3 5
+# Default-Stop: 0 6
+# Short-Description: start and stop BOOTH arbitrator daemon
+### END INIT INFO
+
+prog="booth"
+exec="/usr/sbin/$prog"
+type="arbitrator"
+lockfile="/var/run/$prog.pid"
+
+[ -f /etc/sysconfig/$prog ] && . /etc/sysconfig/$prog
+
+. /etc/rc.status
+
+internal_status() {
+ checkproc $exec > /dev/null 2>&1
+ return $?
+}
+
+status() {
+ if internal_status; then
+ echo "Running"
+ return 0
+ else
+ echo "Stopped"
+ return 7
+ fi
+}
+
+start() {
+ [ -x $exec ] || exit 5
+ echo -n $"Starting BOOTH arbitrator daemon: "
+ if ! internal_status; then
+ startproc $exec $type
+ fi
+ rc_status -v
+}
+
+stop() {
+ echo -n $"Stopping BOOTH arbitrator daemon: "
+ killproc -p $lockfile $prog -TERM
+ rc_status -v
+}
+
+wait_for_stop() {
+ while [ -e $lockfile ]; do
+ sleep .5
+ done
+}
+
+restart() {
+ stop
+ wait_for_stop
+ start
+}
+
+case "$1" in
+ start|stop|restart)
+ $1
+ ;;
+ reload|force-reload)
+ restart
+ ;;
+ condrestart|try-restart)
+ [ ! -f "$lockfile" ] || restart
+ ;;
+ status)
+ status $prog
+ ;;
+ *)
+ echo $"Usage: $0 {start|stop|restart|try-restart|condrestart|reload|force-reload|status}"
+ exit 2
+esac
180 script/ocf/booth-site
@@ -0,0 +1,180 @@
+#!/bin/sh
+#
+# Resource Agent for BOOTH site daemon.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+#
+
+#######################################################################
+# Initialization:
+
+. ${OCF_ROOT}/resource.d/heartbeat/.ocf-shellfuncs
+
+#######################################################################
+
+meta_data() {
+ cat <<END
+<?xml version="1.0"?>
+<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
+<resource-agent name="booth-site" version="0.9">
+<version>1.0</version>
+
+<longdesc lang="en">
+This Resource Agent can control the BOOTH site daemon.
+It assumes that the binary booth is in your default PATH.
+In most cases, it should be run as a primitive resource.
+</longdesc>
+<shortdesc lang="en">BOOTH site daemon</shortdesc>
+
+<parameters>
+
+<parameter name="args" unique="1">
+<longdesc lang="en">
+Any additional options to start the BOOTH daemon with
+</longdesc>
+<shortdesc lang="en">BOOTH Options</shortdesc>
+<content type="string" default="" />
+</parameter>
+
+<parameter name="type" unique="1">
+<longdesc lang="en">
+The type of BOOTH daemon which should be started
+</longdesc>
+<shortdesc lang="en">BOOTH Type</shortdesc>
+<content type="string" default="site" />
+</parameter>
+
+<parameter name="daemon" unique="1">
+<longdesc lang="en">
+The daemon to start
+</longdesc>
+<shortdesc lang="en">The daemon to start</shortdesc>
+<content type="string" default="booth" />
+</parameter>
+
+</parameters>
+
+<actions>
+<action name="start" timeout="90" />
+<action name="stop" timeout="100" />
+<action name="monitor" timeout="20" interval="10" depth="0" start-delay="0" />
+<action name="meta-data" timeout="5" />
+<action name="validate-all" timeout="30" />
+</actions>
+</resource-agent>
+END
+}
+
+#######################################################################
+
+booth_usage() {
+ cat <<END
+usage: $0 {start|stop|monitor|validate-all|meta-data}
+
+Expects to have a fully populated OCF RA-compliant environment set.
+END
+}
+
+booth_start() {
+ booth_monitor; rc=$?
+
+ case $rc in
+ $OCF_SUCCESS) return $OCF_SUCCESS;;
+ $OCF_NOT_RUNNING) ;;
+ *) return $OCF_ERR_GENERIC;;
+ esac
+
+ ${OCF_RESKEY_daemon} $OCF_RESKEY_type $OCF_RESKEY_args
+
+ sleep 1
+ controld_monitor
+}
+
+booth_stop() {
+ booth_monitor; rc=$?
+
+ if [ $rc = $OCF_NOT_RUNNING ]; then
+ return $OCF_SUCCESS
+ fi
+
+ killall -TERM ${OCF_RESKEY_daemon}; rc=$?
+
+ if [ $rc != 0 ]; then
+ return $OCF_ERR_GENERIC
+ fi
+
+ rc=$OCF_SUCCESS
+ while [ $rc = $OCF_SUCCESS ]; do
+ booth_monitor; rc=$?
+ sleep 1
+ done
+
+ if [ $rc = $OCF_NOT_RUNNING ]; then
+ rc=$OCF_SUCCESS
+ fi
+
+ return $rc
+}
+
+booth_monitor() {
+ killall -0 ${OCF_RESKEY_daemon}; rc=$?
+
+ case $rc in
+ 0) return $OCF_SUCCESS;;
+ 1) return $OCF_NOT_RUNNING;;
+ *) return $OCF_ERR_GENERIC;;
+ esac
+}
+
+booth_validate() {
+ check_binary ${OCF_RESKEY_daemon}
+
+ case ${OCF_RESKEY_CRM_meta_gloablly_unique} in
+ yes|Yes|true|True|1)
+ ocf_log err "$OCF_RESOURCE_INSTANCE must be configured with the gloablly_unique=false meta attribute"
+ exit $OCF_ERR_CONFIGURED
+ ;;
+ esac
+
+ return $OCF_SUCCESS
+}
+
+: ${OCF_RESKEY_type=site}
+: ${OCF_RESKEY_CRM_meta_gloablly_unique:="false"}
+
+case $__OCF_ACTION in
+meta-data) meta_data
+ exit $OCF_SUCCESS
+ ;;
+start) booth_validate; booth_start;;
+stop) booth_stop;;
+monitor) booth_validate; booth_monitor;;
+validate-all) booth_validate;;
+usage|help) booth_usage
+ exit $OCF_SUCCESS
+ ;;
+*) booth_usage
+ exit $OCF_ERR_UNIMPLEMENTED
+ ;;
+esac
+rc=$?
+
+exit $rc
+
54 src/Makefile
@@ -0,0 +1,54 @@
+BIN_TARGET = booth
+
+HEADER_TARGET = booth.h config.h list.h log.h pacemaker.h paxos.h paxos_lease.h ticket.h timer.h transport.h
+
+BIN_SOURCE = config.c log.c main.c pacemaker.c paxos.c paxos_lease.c ticket.c timer.c transport.c
+
+CFLAGS += -D_GNU_SOURCE -g \
+ -Wall \
+ -Wformat \
+ -Wformat-security \
+ -Wmissing-prototypes \
+ -Wnested-externs \
+ -Wpointer-arith \
+ -Wextra -Wshadow \
+ -Wcast-align \
+ -Wwrite-strings \
+ -Waggregate-return \
+ -Wstrict-prototypes \
+ -Winline \
+ -Wredundant-decls \
+ -Wno-sign-compare \
+ -Wp,-D_FORTIFY_SOURCE=2 \
+ -fexceptions \
+ -fasynchronous-unwind-tables \
+ -fdiagnostics-show-option
+
+BIN_LDFLAGS = -lpthread
+
+$(BIN_TARGET): $(BIN_SOURCE)
+ $(CC) $(CFLAGS) $(BIN_LDFLAGS) $(BIN_SOURCE) -o $@
+
+clean:
+ rm -f $(BIN_TARGET)
+ rm -f cscope.*
+
+INSTALL=$(shell which install)
+
+BIN_DIR=/usr/sbin
+HEADER_DIR=/usr/include
+
+.PHONY: install
+install: $(BIN_TARGET)
+ mkdir -p $(DESTDIR)/$(BIN_DIR)
+ mkdir -p $(DESTDIR)/$(HEADER_DIR)
+ $(INSTALL) -c -m 755 $(BIN_TARGET) $(DESTDIR)/$(BIN_DIR)
+ $(INSTALL) -c -m 644 $(HEADER_TARGET) $(DESTDIR)/$(HEADER_DIR)
+
+.PHONY: cscope
+cscope:
+ rm -f cscope.*
+ echo "-k" >> cscope.files
+ find . -name '*.[ch]' >> cscope.files
+ cscope -b
+
81 src/booth.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2011 Jiaju Zhang <jjzhang@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _BOOTH_H
+#define _BOOTH_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#define BOOTH_LOG_DUMP_SIZE (1024*1024)
+
+#define BOOTH_RUN_DIR "/var/run"
+#define BOOTH_LOG_DIR "/var/log"
+#define BOOTH_LOGFILE_NAME "booth.log"
+#define BOOTH_LOCKFILE_NAME "booth.pid"
+#define BOOTH_DEFAULT_CONF "/etc/sysconfig/booth"
+
+#define DAEMON_NAME "booth"
+#define BOOTH_NAME_LEN 63
+
+#define BOOTHC_SOCK_PATH "boothc_lock"
+#define BOOTH_PROTO_FAMILY AF_INET
+#define BOOTH_CMD_PORT 22075
+
+#define BOOTHC_MAGIC 0x5F1BA08C
+#define BOOTHC_VERSION 0x00010000
+
+#define BOOTHC_OPT_FORCE 0x00000001
+
+struct boothc_header {
+ uint32_t magic;
+ uint32_t version;
+ uint32_t cmd;
+ uint32_t option;
+ uint32_t expiry;
+ uint32_t len;
+ uint32_t result;
+};
+
+typedef enum {
+ BOOTHC_CMD_LIST = 1,
+ BOOTHC_CMD_GRANT,
+ BOOTHC_CMD_REVOKE,
+} cmd_request_t;
+
+typedef enum {
+ BOOTHC_RLT_ASYNC = 1,
+ BOOTHC_RLT_SYNC_SUCC,
+ BOOTHC_RLT_SYNC_FAIL,
+ BOOTHC_RLT_INVALID_ARG,
+ BOOTHC_RLT_REMOTE_OP,
+} cmd_result_t;
+
+struct client {
+ int fd;
+ void *workfn;
+ void *deadfn;
+};
+
+int client_add(int fd, void (*workfn)(int ci), void (*deadfn)(int ci));
+int do_read(int fd, void *buf, size_t count);
+int do_write(int fd, void *buf, size_t count);
+void process_connection(int ci);
+
+#endif /* _BOOTH_H */
270 src/config.c
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2011 Jiaju Zhang <jjzhang@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include "booth.h"
+#include "config.h"
+#include "log.h"
+
+static int ticket_size = 0;
+
+static int ticket_realloc(void)
+{
+ void *p;
+
+ booth_conf = realloc(booth_conf, sizeof(struct booth_config)
+ + (ticket_size + TICKET_ALLOC)
+ * sizeof(struct ticket_config));
+ if (!booth_conf) {
+ log_error("can't alloc more booth config");
+ return -ENOMEM;
+ }
+
+ p = booth_conf + sizeof(struct booth_config)
+ + ticket_size * sizeof(struct ticket_config);
+ memset(p, 0, TICKET_ALLOC * sizeof(struct ticket_config));
+ ticket_size += TICKET_ALLOC;
+
+ return 0;
+}
+
+int read_config(const char *path)
+{
+ char line[1024];
+ FILE *fp;
+ char *s, *k, *v, *e, *w, *c;
+ int quo, equ, ischar, i;
+ int lineno = 0;
+
+ fp = fopen(path, "r");
+ if (!fp) {
+ log_error("failed to open %s: %s", path, strerror(errno));
+ return -1;
+ }
+
+ booth_conf = malloc(sizeof(struct booth_config)
+ + TICKET_ALLOC * sizeof(struct ticket_config));
+ if (!booth_conf) {
+ log_error("failed to alloc memory for booth config");
+ return -ENOMEM;
+ }
+ memset(booth_conf, 0, sizeof(struct booth_config)
+ + TICKET_ALLOC * sizeof(struct ticket_config));
+ ticket_size = TICKET_ALLOC;
+
+ while (fgets(line, sizeof(line), fp)) {
+ lineno++;
+ s = line;
+ while (*s == ' ')
+ s++;
+ if (*s == '#' || *s == '\n')
+ continue;
+ if (*s == '-' || *s == '.' || *s =='/'
+ || *s == '+' || *s == '(' || *s == ')'
+ || *s == ':' || *s == ',' || *s == '@'
+ || *s == '=' || *s == '"') {
+ log_error("invalid key name in config file "
+ "('%c', lineno %d)", *s, lineno);
+ goto out;
+ }
+ k = s;
+ v = NULL;
+ quo = 0;
+ equ = 0;
+ ischar = 0;
+ while (*s != '\n') {
+ if (!(*s >='a' && *s <= 'z')
+ && !(*s >= 'A' && *s <= 'Z')
+ && !(*s >= '0' && *s <= '9')
+ && !(*s == '_')
+ && !(*s == '-')
+ && !(*s == '.')
+ && !(*s == '/')
+ && !(*s == ' ')
+ && !(*s == '+')
+ && !(*s == '(')
+ && !(*s == ')')
+ && !(*s == ':')
+ && !(*s == ',')
+ && !(*s == '@')
+ && !(*s == '=')
+ && !(*s == '"')) {
+ log_error("invalid character ('%c', lineno %d)"
+ " in config file", *s, lineno);
+ goto out;
+ }
+ if (*s == '=' && !equ) {
+ equ = 1;
+ *s = '\0';
+ v = s + 1;
+ } else if (*s == '=' && equ && !quo) {
+ log_error("invalid config file format "
+ "(lineno %d)", lineno);
+ goto out;
+ } else if ((*s == '_' || *s == '-' || *s == '.')
+ && equ && !quo) {
+ log_error("invalid config file format "
+ "(lineno %d)", lineno);
+ goto out;
+ } else if ((*s == '/' || *s == ' ' || *s == '+'
+ || *s == '(' || *s == ')' || *s == ':'
+ || *s == ',' || *s == '@') && !quo) {
+ log_error("invalid config file format "
+ "(lineno %d)", lineno);
+ goto out;
+ } else if (*s == '"' && !equ) {
+ log_error("invalid config file format "
+ "(lineno %d)", lineno);
+ goto out;
+ } else if (*s == '"' && !quo) {
+ quo = 1;
+ if (v) {
+ v++;
+ ischar = 1;
+ }
+ } else if (*s == '"' && quo) {
+ quo = 0;
+ *s = '\0';
+ }
+ s++;
+ }
+ if (!equ || quo) {
+ log_error("invalid config file format (lineno %d)",
+ lineno);
+ goto out;
+ }
+ if (!ischar)
+ *s = '\0';
+
+ if (strlen(k) > BOOTH_NAME_LEN
+ || strlen(v) > BOOTH_NAME_LEN) {
+ log_error("key/value too long");
+ goto out;
+ }
+
+ if (!strcmp(k, "transport")) {
+ if (!strcmp(v, "UDP"))
+ booth_conf->proto = UDP;
+ else if (!strcmp(v, "SCTP"))
+ booth_conf->proto = SCTP;
+ else {
+ log_error("invalid transport protocol");
+ goto out;
+ }
+ }
+
+ if (!strcmp(k, "port"))
+ booth_conf->port = atoi(v);
+
+ if (!strcmp(k, "site")) {
+ if (booth_conf->node_count == MAX_NODES) {
+ log_error("too many nodes");
+ goto out;
+ }
+ booth_conf->node[booth_conf->node_count].family =
+ BOOTH_PROTO_FAMILY;
+ booth_conf->node[booth_conf->node_count].type = SITE;
+ booth_conf->node[booth_conf->node_count].nodeid =
+ booth_conf->node_count;
+ strcpy(booth_conf->node[booth_conf->node_count++].addr,
+ v);
+ }
+
+ if (!strcmp(k, "arbitrator")) {
+ if (booth_conf->node_count == MAX_NODES) {
+ log_error("too many nodes");
+ goto out;
+ }
+ booth_conf->node[booth_conf->node_count].family =
+ BOOTH_PROTO_FAMILY;
+ booth_conf->node[booth_conf->node_count].type =
+ ARBITRATOR;
+ booth_conf->node[booth_conf->node_count].nodeid =
+ booth_conf->node_count;
+ strcpy(booth_conf->node[booth_conf->node_count++].addr,
+ v);
+ }
+
+ if (!strcmp(k, "ticket")) {
+ int count = booth_conf->ticket_count;
+ if (booth_conf->ticket_count == ticket_size) {
+ if (ticket_realloc() < 0)
+ goto out;
+ }
+ e = index(v, ';');
+ w = rindex(v, ';');
+ if (!e)
+ strcpy(booth_conf->ticket[count].name, v);
+ else if (e && e == w) {
+ *e++ = '\0';
+ while (*e == ' ')
+ e++;
+ strcpy(booth_conf->ticket[count].name, v);
+ booth_conf->ticket[count].expiry = atoi(e);
+ } else {
+ *e++ = '\0';
+ *w++ = '\0';
+ while (*e == ' ')
+ e++;
+ while (*w == ' ')
+ w++;
+ strcpy(booth_conf->ticket[count].name, v);
+ booth_conf->ticket[count].expiry = atoi(e);
+ i = 0;
+ while ((c = index(w, ','))) {
+ *c++ = '\0';
+ booth_conf->ticket[count].weight[i++]
+ = atoi(w);
+ while (*c == ' ')
+ c++;
+ w = c;
+ if (i == MAX_NODES) {
+ log_error("too many weights");
+ break;
+ }
+ }
+ }
+ booth_conf->ticket_count++;
+ }
+ }
+ return 0;
+
+out:
+ free(booth_conf);
+ return -1;
+}
+
+int check_config(int type)
+{
+// int i;
+
+ if (!booth_conf)
+ return -1;
+
+/* for (i = 0; i < booth_conf->node_count; i++) {
+ if (booth_conf->node[i].local && booth_conf->node[i].type ==
+ type)
+ return 0;
+ }
+
+ return -1;*/
+ return 0;
+}
50 src/config.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2011 Jiaju Zhang <jjzhang@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _CONFIG_H
+#define _CONFIG_H
+
+#include <stdint.h>
+#include "booth.h"
+#include "transport.h"
+
+#define MAX_NODES 16
+#define TICKET_ALLOC 16
+
+struct ticket_config {
+ int weight[MAX_NODES];
+ int expiry;
+ char name[BOOTH_NAME_LEN];
+};
+
+struct booth_config {
+ int node_count;
+ int ticket_count;
+ int proto;
+ uint16_t port;
+ struct booth_node node[MAX_NODES];
+ struct ticket_config ticket[0];
+};
+
+struct booth_config *booth_conf;
+
+int read_config(const char *path);
+
+int check_config(int type);
+
+#endif /* _CONFIG_H */
568 src/list.h
@@ -0,0 +1,568 @@
+/* Copied from linux kernel */
+
+#ifndef _LINUX_LIST_H
+#define _LINUX_LIST_H
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+/**
+ * Get offset of a member
+ */
+#define offsetof(type, member) ((size_t) &((type *)0)->member)
+
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ *
+ * @ptr: the pointer to the member.
+ * @type: the type of the container struct this is embedded in.
+ * @member: the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+
+#define LIST_POISON1 ((void *) 0x00100100)
+#define LIST_POISON2 ((void *) 0x00200200)
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+ struct list_head name = LIST_HEAD_INIT(name)
+
+static inline void INIT_LIST_HEAD(struct list_head *list)
+{
+ list->next = list;
+ list->prev = list;
+}
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head *new,
+ struct list_head *prev,
+ struct list_head *next)
+{
+ next->prev = new;
+ new->next = next;
+ new->prev = prev;
+ prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head, head->next);
+}
+
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty() on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void list_del(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ entry->next = LIST_POISON1;
+ entry->prev = LIST_POISON2;
+}
+
+/**
+ * list_replace - replace old entry by new one
+ * @old : the element to be replaced
+ * @new : the new element to insert
+ *
+ * If @old was empty, it will be overwritten.
+ */
+static inline void list_replace(struct list_head *old,
+ struct list_head *new)
+{
+ new->next = old->next;
+ new->next->prev = new;
+ new->prev = old->prev;
+ new->prev->next = new;
+}
+
+static inline void list_replace_init(struct list_head *old,
+ struct list_head *new)
+{
+ list_replace(old, new);
+ INIT_LIST_HEAD(old);
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static inline void list_del_init(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ INIT_LIST_HEAD(entry);
+}
+
+/**
+ * list_move - delete from one list and add as another's head
+ * @list: the entry to move
+ * @head: the head that will precede our entry
+ */
+static inline void list_move(struct list_head *list, struct list_head *head)
+{
+ __list_del(list->prev, list->next);
+ list_add(list, head);
+}
+
+/**
+ * list_move_tail - delete from one list and add as another's tail
+ * @list: the entry to move
+ * @head: the head that will follow our entry
+ */
+static inline void list_move_tail(struct list_head *list,
+ struct list_head *head)
+{
+ __list_del(list->prev, list->next);
+ list_add_tail(list, head);
+}
+
+/**
+ * list_is_last - tests whether @list is the last entry in list @head
+ * @list: the entry to test
+ * @head: the head of the list
+ */
+static inline int list_is_last(const struct list_head *list,
+ const struct list_head *head)
+{
+ return list->next == head;
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static inline int list_empty(const struct list_head *head)
+{
+ return head->next == head;
+}
+
+/**
+ * list_empty_careful - tests whether a list is empty and not being modified
+ * @head: the list to test
+ *
+ * Description:
+ * tests whether a list is empty _and_ checks that no other CPU might be
+ * in the process of modifying either member (next or prev)
+ *
+ * NOTE: using list_empty_careful() without synchronization
+ * can only be safe if the only activity that can happen
+ * to the list entry is list_del_init(). Eg. it cannot be used
+ * if another CPU could re-list_add() it.
+ */
+static inline int list_empty_careful(const struct list_head *head)
+{
+ struct list_head *next = head->next;
+ return (next == head) && (next == head->prev);
+}
+
+/**
+ * list_rotate_left - rotate the list to the left
+ * @head: the head of the list
+ */
+static inline void list_rotate_left(struct list_head *head)
+{
+ struct list_head *first;
+
+ if (!list_empty(head)) {
+ first = head->next;
+ list_move_tail(first, head);
+ }
+}
+
+/**
+ * list_is_singular - tests whether a list has just one entry.
+ * @head: the list to test.
+ */
+static inline int list_is_singular(const struct list_head *head)
+{
+ return !list_empty(head) && (head->next == head->prev);
+}
+
+static inline void __list_cut_position(struct list_head *list,
+ struct list_head *head, struct list_head *entry)
+{
+ struct list_head *new_first = entry->next;
+ list->next = head->next;
+ list->next->prev = list;
+ list->prev = entry;
+ entry->next = list;
+ head->next = new_first;
+ new_first->prev = head;
+}
+
+/**
+ * list_cut_position - cut a list into two
+ * @list: a new list to add all removed entries
+ * @head: a list with entries
+ * @entry: an entry within head, could be the head itself
+ * and if so we won't cut the list
+ *
+ * This helper moves the initial part of @head, up to and
+ * including @entry, from @head to @list. You should
+ * pass on @entry an element you know is on @head. @list
+ * should be an empty list or a list you do not care about
+ * losing its data.
+ *
+ */
+static inline void list_cut_position(struct list_head *list,
+ struct list_head *head, struct list_head *entry)
+{
+ if (list_empty(head))
+ return;
+ if (list_is_singular(head) &&
+ (head->next != entry && head != entry))
+ return;
+ if (entry == head)
+ INIT_LIST_HEAD(list);
+ else
+ __list_cut_position(list, head, entry);
+}
+
+static inline void __list_splice(const struct list_head *list,
+ struct list_head *prev,
+ struct list_head *next)
+{
+ struct list_head *first = list->next;
+ struct list_head *last = list->prev;
+
+ first->prev = prev;
+ prev->next = first;
+
+ last->next = next;
+ next->prev = last;
+}
+
+/**
+ * list_splice - join two lists, this is designed for stacks
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static inline void list_splice(const struct list_head *list,
+ struct list_head *head)
+{
+ if (!list_empty(list))
+ __list_splice(list, head, head->next);
+}
+
+/**
+ * list_splice_tail - join two lists, each list being a queue
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static inline void list_splice_tail(struct list_head *list,
+ struct list_head *head)
+{
+ if (!list_empty(list))
+ __list_splice(list, head->prev, head);
+}
+
+/**
+ * list_splice_init - join two lists and reinitialise the emptied list.
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_init(struct list_head *list,
+ struct list_head *head)
+{
+ if (!list_empty(list)) {
+ __list_splice(list, head, head->next);
+ INIT_LIST_HEAD(list);
+ }
+}
+
+/**
+ * list_splice_tail_init - join two lists and reinitialise the emptied list
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * Each of the lists is a queue.
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_tail_init(struct list_head *list,
+ struct list_head *head)
+{
+ if (!list_empty(list)) {
+ __list_splice(list, head->prev, head);
+ INIT_LIST_HEAD(list);
+ }
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr: the &struct list_head pointer.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+ container_of(ptr, type, member)
+
+/**
+ * list_first_entry - get the first element from a list
+ * @ptr: the list head to take the element from.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Note, that list is expected to be not empty.
+ */
+#define list_first_entry(ptr, type, member) \
+ list_entry((ptr)->next, type, member)
+
+/**
+ * list_for_each - iterate over a list
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @head: the head for your list.
+ */
+#define list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * __list_for_each - iterate over a list
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @head: the head for your list.
+ *
+ * This variant differs from list_for_each() in that it's the
+ * simplest possible list iteration code, no prefetching is done.
+ * Use this for code that knows the list to be very short (empty
+ * or 1 entry) most of the time.
+ */
+#define __list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_prev - iterate over a list backwards
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @head: the head for your list.
+ */
+#define list_for_each_prev(pos, head) \
+ for (pos = (head)->prev; pos != (head); pos = pos->prev)
+
+/**
+ * list_for_each_safe - iterate over a list safe against removal of list entry
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @n: another &struct list_head to use as temporary storage
+ * @head: the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, n = pos->next)
+
+/**
+ * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @n: another &struct list_head to use as temporary storage
+ * @head: the head for your list.
+ */
+#define list_for_each_prev_safe(pos, n, head) \
+ for (pos = (head)->prev, n = pos->prev; \
+ pos != (head); \
+ pos = n, n = pos->prev)
+
+/**
+ * list_for_each_entry - iterate over list of given type
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry(pos, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_reverse - iterate backwards over list of given type.
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_reverse(pos, head, member) \
+ for (pos = list_entry((head)->prev, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.prev, typeof(*pos), member))
+
+/**
+ * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue()
+ * @pos: the type * to use as a start point
+ * @head: the head of the list
+ * @member: the name of the list_struct within the struct.
+ *
+ * Prepares a pos entry for use as a start point in list_for_each_entry_continue().
+ */
+#define list_prepare_entry(pos, head, member) \
+ ((pos) ? : list_entry(head, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_continue - continue iteration over list of given type
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Continue to iterate over list of given type, continuing after
+ * the current position.
+ */
+#define list_for_each_entry_continue(pos, head, member) \
+ for (pos = list_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_continue_reverse - iterate backwards from the given point
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Start to iterate over list of given type backwards, continuing after
+ * the current position.
+ */
+#define list_for_each_entry_continue_reverse(pos, head, member) \
+ for (pos = list_entry(pos->member.prev, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.prev, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_from - iterate over list of given type from the current point
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Iterate over list of given type, continuing from current position.
+ */
+#define list_for_each_entry_from(pos, head, member) \
+ for (; &pos->member != (head); \
+ pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos: the type * to use as a loop cursor.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member), \
+ n = list_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_continue - continue list iteration safe against removal
+ * @pos: the type * to use as a loop cursor.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Iterate over list of given type, continuing after current point,
+ * safe against removal of list entry.
+ */
+#define list_for_each_entry_safe_continue(pos, n, head, member) \
+ for (pos = list_entry(pos->member.next, typeof(*pos), member), \
+ n = list_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_from - iterate over list from current point safe against removal
+ * @pos: the type * to use as a loop cursor.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Iterate over list of given type from current point, safe against
+ * removal of list entry.
+ */
+#define list_for_each_entry_safe_from(pos, n, head, member) \
+ for (n = list_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_reverse - iterate backwards over list safe against removal
+ * @pos: the type * to use as a loop cursor.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Iterate backwards over list of given type, safe against removal
+ * of list entry.
+ */
+#define list_for_each_entry_safe_reverse(pos, n, head, member) \
+ for (pos = list_entry((head)->prev, typeof(*pos), member), \
+ n = list_entry(pos->member.prev, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = list_entry(n->member.prev, typeof(*n), member))
+
+/**
+ * list_safe_reset_next - reset a stale list_for_each_entry_safe loop
+ * @pos: the loop cursor used in the list_for_each_entry_safe loop
+ * @n: temporary storage used in list_for_each_entry_safe
+ * @member: the name of the list_struct within the struct.
+ *
+ * list_safe_reset_next is not safe to use in general if the list may be
+ * modified concurrently (eg. the lock is dropped in the loop body). An
+ * exception to this is if the cursor element (pos) is pinned in the list,
+ * and list_safe_reset_next is called after re-taking the lock and before
+ * completing the current iteration of the loop body.
+ */
+#define list_safe_reset_next(pos, n, member) \
+ n = list_entry(pos->member.next, typeof(*pos), member)
+
+#endif
+
245 src/log.c
@@ -0,0 +1,245 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <limits.h>
+#include <pthread.h>
+
+#include "booth.h"
+#include "log.h"
+
+#define LOG_STR_LEN 256
+static char log_str[LOG_STR_LEN];
+
+static pthread_t thread_handle;
+
+static pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t log_cond = PTHREAD_COND_INITIALIZER;
+
+static char log_dump[BOOTH_LOG_DUMP_SIZE];
+static unsigned int log_point;
+static unsigned int log_wrap;
+
+static char logfile_path[PATH_MAX];
+static FILE *logfile_fp;
+
+struct entry {
+ int level;
+ char str[LOG_STR_LEN];
+};
+
+#define BOOTH_LOG_DEFAULT_ENTRIES 4096
+static struct entry *log_ents;
+static unsigned int log_num_ents = BOOTH_LOG_DEFAULT_ENTRIES;
+static unsigned int log_head_ent; /* add at head */
+static unsigned int log_tail_ent; /* remove from tail */
+static unsigned int log_dropped;
+static unsigned int log_pending_ents;
+static unsigned int log_thread_done;
+
+extern int log_logfile_priority;
+extern int log_syslog_priority;
+extern int log_stderr_priority;
+
+static void _log_save_dump(int level __attribute__((unused)), int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ log_dump[log_point++] = log_str[i];
+
+ if (log_point == BOOTH_LOG_DUMP_SIZE) {
+ log_point = 0;
+ log_wrap = 1;
+ }
+ }
+}
+
+static void _log_save_ent(int level, int len)
+{
+ struct entry *e;
+
+ if (!log_ents)
+ return;
+
+ if (log_pending_ents == log_num_ents) {
+ log_dropped++;
+ return;
+ }
+
+ e = &log_ents[log_head_ent++];
+ log_head_ent = log_head_ent % log_num_ents;
+ log_pending_ents++;
+
+ e->level = level;
+ memcpy(e->str, log_str, len);
+}
+
+void log_level(int level, const char *fmt, ...)
+{
+ va_list ap;
+ char name[BOOTH_NAME_LEN + 1];
+ int ret, pos = 0;
+ int len = LOG_STR_LEN - 2; /* leave room for \n\0 */
+
+ memset(name, 0, sizeof(name));
+
+ pthread_mutex_lock(&log_mutex);
+
+ ret = snprintf(log_str + pos, len - pos, "%ld %s ",
+ time(NULL), name);
+ pos += ret;
+
+ va_start(ap, fmt);
+ ret = vsnprintf(log_str + pos, len - pos, fmt, ap);
+ va_end(ap);
+
+ if (ret >= len - pos)
+ pos = len - 1;
+ else
+ pos += ret;
+
+ log_str[pos++] = '\n';
+ log_str[pos++] = '\0';
+
+ /*
+ * save all messages in circular buffer "log_dump" that can be
+ * sent over unix socket
+ */
+
+ _log_save_dump(level, pos - 1);
+
+ /*
+ * save some messages in circular array "log_ents" that a thread
+ * writes to logfile/syslog
+ */
+
+ if (level <= log_logfile_priority || level <= log_syslog_priority)
+ _log_save_ent(level, pos);
+
+ if (level <= log_stderr_priority)
+ fprintf(stderr, "%s", log_str);
+
+ pthread_cond_signal(&log_cond);
+ pthread_mutex_unlock(&log_mutex);
+}
+
+static void write_entry(int level, char *str)
+{
+ if ((level <= log_logfile_priority) && logfile_fp) {
+ fprintf(logfile_fp, "%s", str);
+ fflush(logfile_fp);
+ }
+ if (level <= log_syslog_priority)
+ syslog(level, "%s", str);
+}
+
+static void write_dropped(int level, int num)
+{
+ char str[LOG_STR_LEN];
+ sprintf(str, "dropped %d entries", num);
+ write_entry(level, str);
+}
+
+static void *log_thread_fn(void *arg __attribute__((unused)))
+{
+ char str[LOG_STR_LEN];
+ struct entry *e;
+ int level, prev_dropped = 0;
+
+ while (1) {
+ pthread_mutex_lock(&log_mutex);
+ while (log_head_ent == log_tail_ent) {
+ if (log_thread_done) {
+ pthread_mutex_unlock(&log_mutex);
+ goto out;
+ }
+ pthread_cond_wait(&log_cond, &log_mutex);
+ }
+
+ e = &log_ents[log_tail_ent++];
+ log_tail_ent = log_tail_ent % log_num_ents;
+ log_pending_ents--;
+
+ memcpy(str, e->str, LOG_STR_LEN);
+ level = e->level;
+
+ prev_dropped = log_dropped;
+ log_dropped = 0;
+ pthread_mutex_unlock(&log_mutex);
+
+ if (prev_dropped) {
+ write_dropped(level, prev_dropped);
+ prev_dropped = 0;
+ }
+
+ write_entry(level, str);
+ }
+out:
+ pthread_exit(NULL);
+}
+
+
+int setup_logging(void)
+{
+ int fd, rv;
+
+ snprintf(logfile_path, PATH_MAX, "%s/%s", BOOTH_LOG_DIR,
+ BOOTH_LOGFILE_NAME);
+
+ logfile_fp = fopen(logfile_path, "a+");
+ if (logfile_fp) {
+ fd = fileno(logfile_fp);
+ fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
+ }
+
+ log_ents = malloc(log_num_ents * sizeof(struct entry));
+ if (!log_ents) {
+ fclose(logfile_fp);
+ logfile_fp = NULL;
+ return -1;
+ }
+ memset(log_ents, 0, log_num_ents * sizeof(struct entry));
+
+ openlog(DAEMON_NAME, LOG_CONS | LOG_PID, LOG_DAEMON);
+
+ rv = pthread_create(&thread_handle, NULL, log_thread_fn, NULL);
+ if (rv)
+ return -1;
+
+ return 0;
+}
+
+void close_logging(void)
+{
+ pthread_mutex_lock(&log_mutex);
+ log_thread_done = 1;
+ pthread_cond_signal(&log_cond);
+ pthread_mutex_unlock(&log_mutex);
+ pthread_join(thread_handle, NULL);
+
+ pthread_mutex_lock(&log_mutex);
+ closelog();
+ if (logfile_fp) {
+ fclose(logfile_fp);
+ logfile_fp = NULL;
+ }
+ pthread_mutex_unlock(&log_mutex);
+}
32 src/log.h
@@ -0,0 +1,32 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LOG_H
+#define _LOG_H
+
+#include <syslog.h>
+
+void log_level(int level, const char *fmt, ...)
+ __attribute__((format(printf, 2, 3)));
+
+int setup_logging(void);
+void close_logging(void);
+
+#define log_debug(fmt, args...) log_level(LOG_DEBUG, fmt, ##args)
+#define log_info(fmt, args...) log_level(LOG_INFO, fmt, ##args)
+#define log_error(fmt, args...) log_level(LOG_ERR, fmt, ##args)
+
+#endif /* _LOG_H */
1,000 src/main.c
@@ -0,0 +1,1000 @@
+/*
+ * Copyright (C) 2011 Jiaju Zhang <jjzhang@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sched.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/file.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/poll.h>
+#include <fcntl.h>
+#include <string.h>
+#include "log.h"
+#include "booth.h"
+#include "config.h"
+#include "transport.h"
+#include "timer.h"
+#include "pacemaker.h"
+#include "ticket.h"
+
+#define RELEASE_VERSION "1.0"
+
+#define CLIENT_NALLOC 32
+
+int log_logfile_priority = LOG_INFO;
+int log_syslog_priority = LOG_ERR;
+int log_stderr_priority = LOG_ERR;
+
+static int client_maxi;
+static int client_size = 0;
+struct client *client = NULL;
+struct pollfd *pollfd = NULL;
+
+int poll_timeout = -1;
+
+typedef enum {
+ ACT_ARBITRATOR = 1,
+ ACT_SITE,
+ ACT_CLIENT,
+} booth_role_t;
+
+typedef enum {
+ OP_LIST = 1,
+ OP_GRANT,
+ OP_REVOKE,
+} operation_t;
+
+struct command_line {
+ int type; /* ACT_ */
+ int op; /* OP_ */
+ int debug;
+ int force;
+ int expiry;
+ char site[BOOTH_NAME_LEN];
+ char ticket[BOOTH_NAME_LEN];
+};
+
+static struct command_line cl;
+
+int do_read(int fd, void *buf, size_t count)
+{
+ int rv, off = 0;
+
+ while (off < count) {
+ rv = read(fd, (char *)buf + off, count - off);
+ if (rv == 0)
+ return -1;
+ if (rv == -1 && errno == EINTR)
+ continue;
+ if (rv == -1)
+ return -1;
+ off += rv;
+ }
+ return 0;
+}
+
+int do_write(int fd, void *buf, size_t count)
+{
+ int rv, off = 0;
+
+retry:
+ rv = write(fd, (char *)buf + off, count);
+ if (rv == -1 && errno == EINTR)
+ goto retry;
+ if (rv < 0) {
+ log_error("write errno %d", errno);
+ return rv;
+ }
+
+ if (rv != count) {
+ count -= rv;
+ off += rv;
+ goto retry;
+ }
+ return 0;
+}
+
+static int do_connect(const char *sock_path)
+{
+ struct sockaddr_un sun;
+ socklen_t addrlen;
+ int rv, fd;
+
+ fd = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0)
+ goto out;
+
+ memset(&sun, 0, sizeof(sun));
+ sun.sun_family = AF_UNIX;
+ strcpy(&sun.sun_path[1], sock_path);
+ addrlen = sizeof(sa_family_t) + strlen(sun.sun_path+1) + 1;
+
+ rv = connect(fd, (struct sockaddr *) &sun, addrlen);
+ if (rv < 0) {
+ close(fd);
+ fd = rv;
+ }
+out:
+ return fd;
+}
+
+static void init_header(struct boothc_header *h, int cmd, int option,
+ int expiry, int result, int data_len)
+{
+ memset(h, 0, sizeof(struct boothc_header));
+
+ h->magic = BOOTHC_MAGIC;
+ h->version = BOOTHC_VERSION;
+ h->len = data_len;
+ h->cmd = cmd;
+ h->option = option;
+ h->expiry = expiry;
+ h->result = result;
+}
+
+static void client_alloc(void)
+{
+ int i;
+
+ if (!client) {
+ client = malloc(CLIENT_NALLOC * sizeof(struct client));
+ pollfd = malloc(CLIENT_NALLOC * sizeof(struct pollfd));
+ } else {
+ client = realloc(client, (client_size + CLIENT_NALLOC) *
+ sizeof(struct client));
+ pollfd = realloc(pollfd, (client_size + CLIENT_NALLOC) *
+ sizeof(struct pollfd));
+ if (!pollfd)
+ log_error("can't alloc for pollfd");
+ }
+ if (!client || !pollfd)
+ log_error("can't alloc for client array");
+
+ for (i = client_size; i < client_size + CLIENT_NALLOC; i++) {
+ client[i].workfn = NULL;
+ client[i].deadfn = NULL;
+ client[i].fd = -1;
+ pollfd[i].fd = -1;
+ pollfd[i].revents = 0;
+ }
+ client_size += CLIENT_NALLOC;
+}
+
+static void client_dead(int ci)
+{
+ close(client[ci].fd);
+ client[ci].workfn = NULL;
+ client[ci].fd = -1;
+ pollfd[ci].fd = -1;
+}
+
+int client_add(int fd, void (*workfn)(int ci), void (*deadfn)(int ci))
+{
+ int i;
+
+ if (!client)
+ client_alloc();
+again:
+ for (i = 0; i < client_size; i++) {
+ if (client[i].fd == -1) {
+ client[i].workfn = workfn;
+ if (deadfn)
+ client[i].deadfn = deadfn;
+ else
+ client[i].deadfn = client_dead;
+ client[i].fd = fd;
+ pollfd[i].fd = fd;
+ pollfd[i].events = POLLIN;
+ if (i > client_maxi)
+ client_maxi = i;
+ return i;
+ }
+ }
+
+ client_alloc();
+ goto again;
+}
+
+static int setup_listener(const char *sock_path)
+{
+ struct sockaddr_un addr;
+ socklen_t addrlen;
+ int rv, s;
+
+ s = socket(AF_LOCAL, SOCK_STREAM, 0);
+ if (s < 0) {
+ log_error("socket error %d %d", s, errno);
+ return s;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+
+ addr.sun_family = AF_LOCAL;
+ strcpy(&addr.sun_path[1], sock_path);
+ addrlen = sizeof(sa_family_t) + strlen(addr.sun_path+1) + 1;
+
+ rv = bind(s, (struct sockaddr *) &addr, addrlen);
+ if (rv < 0) {
+ log_error("bind error %d %d", rv, errno);
+ close(s);
+ return rv;
+ }
+
+ rv = listen(s, 5);
+ if (rv < 0) {
+ log_error("listen error %d %d", rv, errno);
+ close(s);
+ return rv;
+ }
+ return s;
+}
+
+void process_connection(int ci)
+{
+ struct boothc_header h;
+ char *data = NULL;
+ char *site, *ticket;
+ int local, rv;
+
+ rv = do_read(client[ci].fd, &h, sizeof(h));
+ if (rv < 0) {
+ log_error("connection %d read error %d", ci, rv);
+ return;
+ }
+ if (h.magic != BOOTHC_MAGIC) {
+ log_error("connection %d magic error %x", ci, h.magic);
+ return;
+ }
+ if (h.version != BOOTHC_VERSION) {
+ log_error("connection %d version error %x", ci, h.version);
+ return;
+ }
+
+ if (h.len) {
+ data = malloc(h.len);
+ if (!data) {
+ log_error("process_connection no mem %u", h.len);
+ return;
+ }
+ memset(data, 0, h.len);
+
+ rv = do_read(client[ci].fd, data, h.len);
+ if (rv < 0) {
+ log_error("connection %d read data error %d", ci, rv);
+ goto out;
+ }
+ }
+
+ switch (h.cmd) {
+ case BOOTHC_CMD_LIST:
+ break;
+
+ case BOOTHC_CMD_GRANT:
+ site = data;
+ ticket = data + BOOTH_NAME_LEN;
+ if (!check_ticket(ticket)) {
+ h.result = BOOTHC_RLT_INVALID_ARG;
+ goto reply;
+ }
+ if (!check_site(site, &local)) {
+ h.result = BOOTHC_RLT_INVALID_ARG;
+ goto reply;
+ }
+ if (local)
+ h.result = grant_ticket(ticket, h.option, h.expiry);
+ else
+ h.result = BOOTHC_RLT_REMOTE_OP;
+ break;
+
+ case BOOTHC_CMD_REVOKE:
+ break;
+
+ default:
+ log_error("connection %d cmd %x unknown", ci, h.cmd);
+ break;
+ }
+
+reply:
+ rv = do_write(client[ci].fd, &h, sizeof(h));
+ if (rv < 0)
+ log_error("connection %d write error %d", ci, rv);
+out:
+ free(data);
+}
+
+static void process_listener(int ci)
+{
+ int fd, i;
+
+ fd = accept(client[ci].fd, NULL, NULL);
+ if (fd < 0) {
+ log_error("process_listener: accept error %d %d", fd, errno);
+ return;
+ }
+
+ i = client_add(fd, process_connection, NULL);
+
+ log_debug("client connection %d fd %d", i, fd);
+}
+
+static int setup_config(int type)
+{
+ int rv;
+
+ rv = read_config(BOOTH_DEFAULT_CONF);
+ if (rv < 0)
+ goto out;
+
+ rv = check_config(type);
+ if (rv < 0)
+ goto out;
+
+out:
+ return rv;
+}
+
+static int setup_transport(void)
+{
+ int rv;
+
+ rv = booth_transport[booth_conf->proto].init(ticket_recv);
+ if (rv < 0)
+ goto out;
+
+ rv = booth_transport[TCP].init(NULL);
+ if (rv < 0)
+ goto out;
+
+out:
+ return rv;
+}
+
+static int setup_timer(void)
+{
+ return timerlist_init();
+}
+
+static void loop(int type)
+{
+ void (*workfn) (int ci);
+ void (*deadfn) (int ci);
+ int rv, i;
+
+ rv = setup_config(type);
+ if (rv < 0)
+ goto out;
+
+ rv = setup_timer();
+ if (rv < 0)
+ goto out;
+
+ rv = setup_transport();
+ if (rv < 0)
+ goto out;
+
+ rv = setup_ticket();
+ if (rv < 0)
+ goto out;
+
+ rv = setup_listener(BOOTHC_SOCK_PATH);
+ if (rv < 0)
+ goto out;
+ client_add(rv, process_listener, NULL);
+
+ while (1) {
+ rv = poll(pollfd, client_maxi + 1, poll_timeout);
+ if (rv == -1 && errno == EINTR)
+ continue;
+ if (rv < 0) {
+ log_error("poll errno %d", errno);
+ goto out;
+ }
+
+ for (i = 0; i <= client_maxi; i++) {
+ if (client[i].fd < 0)
+ continue;
+ if (pollfd[i].revents & POLLIN) {
+ workfn = client[i].workfn;
+ if (workfn)
+ workfn(i);
+ }
+ if (pollfd[i].revents &
+ (POLLERR | POLLHUP | POLLNVAL)) {
+ deadfn = client[i].deadfn;
+ if (deadfn)
+ deadfn(i);
+ }
+ }
+
+ process_timerlist();
+ }
+
+out:
+ return;
+}
+
+static int do_list(void)
+{
+ struct boothc_header h, *rh;
+ char *reply = NULL, *data;
+ int data_len;
+ int fd, rv;
+
+ init_header(&h, BOOTHC_CMD_LIST, 0, 0, 0, 0);
+
+ fd = do_connect(BOOTHC_SOCK_PATH);
+ if (fd < 0) {
+ rv = fd;
+ goto out;
+ }
+
+ rv = do_write(fd, &h, sizeof(h));
+ if (rv < 0)
+ goto out_close;
+
+ reply = malloc(sizeof(struct boothc_header));
+ if (!reply) {
+ rv = -ENOMEM;
+ goto out_close;
+ }
+
+ rv = do_read(fd, reply, sizeof(struct boothc_header));
+ if (rv < 0)
+ goto out_free;
+
+ rh = (struct boothc_header *)reply;
+ data_len = rh->len;
+
+ reply = realloc(reply, sizeof(struct boothc_header) + data_len);
+ if (!reply) {
+ rv = -ENOMEM;
+ goto out_free;
+ }
+ data = reply + sizeof(struct boothc_header);
+ rv = do_read(fd, data, data_len);
+ if (rv < 0)
+ goto out_free;
+
+ do_write(STDOUT_FILENO, data, data_len);
+ rv = 0;
+
+out_free:
+ free(reply);
+out_close:
+ close(fd);
+out:
+ return rv;
+}
+
+static int do_grant(void)
+{
+ char *buf;
+ struct boothc_header *h, reply;
+ int buflen;
+ uint32_t force;
+ int fd, rv;
+
+ buflen = sizeof(struct boothc_header) +
+ sizeof(cl.site) + sizeof(cl.ticket);
+ buf = malloc(buflen);
+ if (!buf) {
+ rv = -ENOMEM;
+ goto out;
+ }
+ h = (struct boothc_header *)buf;
+ if (cl.force)
+ force = BOOTHC_OPT_FORCE;
+ init_header(h, BOOTHC_CMD_GRANT, force, cl.expiry, 0,
+ sizeof(cl.site) + sizeof(cl.ticket));
+ strcpy(buf + sizeof(struct boothc_header), cl.site);
+ strcpy(buf + sizeof(struct boothc_header) + sizeof(cl.site), cl.ticket);
+
+ fd = do_connect(BOOTHC_SOCK_PATH);
+ if (fd < 0) {
+ rv = fd;
+ goto out_free;
+ }
+
+ rv = do_write(fd, buf, buflen);
+ if (rv < 0)
+ goto out_close;
+
+ rv = do_read(fd, &reply, sizeof(struct boothc_header));
+ if (rv < 0)
+ goto out_close;
+
+ if (reply.result == BOOTHC_RLT_INVALID_ARG) {
+ log_info("invalid argument!");
+ rv = -1;
+ goto out_close;
+ }
+
+ if (reply.result == BOOTHC_RLT_REMOTE_OP) {
+ struct booth_node to;
+ int s;
+
+ memset(&to, 0, sizeof(struct booth_node));
+ to.family = BOOTH_PROTO_FAMILY;
+ strcpy(to.addr, cl.site);
+
+ s = booth_transport[TCP].open(&to);
+ if (s < 0)
+ goto out_close;
+
+ rv = booth_transport[TCP].send(s, buf, buflen);
+ if (rv < 0) {
+ booth_transport[TCP].close(s);
+ goto out_close;
+ }
+ rv = booth_transport[TCP].recv(s, &reply,
+ sizeof(struct boothc_header));
+ if (rv < 0) {
+ booth_transport[TCP].close(s);
+ goto out_close;
+ }
+ booth_transport[TCP].close(s);
+ }
+
+ if (reply.result == BOOTHC_RLT_ASYNC) {
+ log_info("grant command sent, but result is async.");
+ rv = 0;
+ } else if (reply.result == BOOTHC_RLT_SYNC_SUCC) {
+ log_info("grant succeeded!");
+ rv = 0;
+ } else if (reply.result == BOOTHC_RLT_SYNC_FAIL) {
+ log_info("grant failed!");
+ rv = 0;
+ } else {
+ log_error("internal error!");
+ rv = -1;
+ }
+
+out_close:
+ close(fd);
+out_free:
+ free(buf);
+out:
+ return rv;
+}
+
+static int do_revoke(void)
+{
+ char *buf;
+ struct boothc_header *h, reply;
+ int buflen;
+ int fd, rv;
+
+ buflen = sizeof(struct boothc_header) + sizeof(cl.ticket);
+ buf = malloc(buflen);
+ if (!buf) {
+ rv = -ENOMEM;
+ goto out;
+ }
+ h = (struct boothc_header *)buf;
+ init_header(h, BOOTHC_CMD_REVOKE, 0, 0, 0, sizeof(cl.ticket));
+ strcpy(buf + sizeof(struct boothc_header), cl.ticket);
+
+ fd = do_connect(BOOTHC_SOCK_PATH);
+ if (fd < 0) {
+ rv = fd;
+ goto out_free;
+ }
+
+ rv = do_write(fd, buf, buflen);
+ if (rv < 0)
+ goto out_close;
+
+ rv = do_read(fd, &reply, sizeof(struct boothc_header));
+ if (rv < 0)
+ goto out_close;
+ if (reply.result == BOOTHC_RLT_ASYNC) {
+ log_info("revoke command sent, but result is async.");
+ rv = 0;
+ } else if (reply.result == BOOTHC_RLT_SYNC_SUCC) {
+ log_info("revoke succeeded!");
+ rv = 0;
+ } else if (reply.result == BOOTHC_RLT_SYNC_FAIL) {
+ log_info("revoke failed!");
+ rv = 0;
+ } else {
+ log_error("internal error!");
+ rv = -1;
+ }
+
+out_close:
+ close(fd);
+out_free:
+ free(buf);
+out:
+ return rv;
+}
+
+static int lockfile(void)
+{
+ char path[PATH_MAX];
+ char buf[16];
+ struct flock lock;
+ int fd, rv;
+
+ snprintf(path, PATH_MAX, "%s/%s", BOOTH_RUN_DIR, BOOTH_LOCKFILE_NAME);
+
+ fd = open(path, O_CREAT|O_WRONLY, 0666);
+ if (fd < 0) {
+ log_error("lockfile open error %s: %s",
+ path, strerror(errno));
+ return -1;
+ }
+
+ lock.l_type = F_WRLCK;
+ lock.l_start = 0;
+ lock.l_whence = SEEK_SET;
+ lock.l_len = 0;
+
+ rv = fcntl(fd, F_SETLK, &lock);
+ if (rv < 0) {
+ log_error("lockfile setlk error %s: %s",
+ path, strerror(errno));
+ goto fail;
+ }
+
+ rv = ftruncate(fd, 0);
+ if (rv < 0) {
+ log_error("lockfile truncate error %s: %s",
+ path, strerror(errno));
+ goto fail;
+ }
+
+ memset(buf, 0, sizeof(buf));
+ snprintf(buf, sizeof(buf), "%d\n", getpid());
+
+ rv = write(fd, buf, strlen(buf));
+ if (rv <= 0) {
+ log_error("lockfile write error %s: %s",
+ path, strerror(errno));
+ goto fail;
+ }
+
+ return fd;
+ fail:
+ close(fd);
+ return -1;
+}
+
+static void unlink_lockfile(int fd)
+{
+ char path[PATH_MAX];
+
+ snprintf(path, PATH_MAX, "%s/%s", BOOTH_RUN_DIR, BOOTH_LOCKFILE_NAME);
+ unlink(path);
+ close(fd);
+}
+
+static void print_usage(void)
+{
+ printf("Usage:\n");
+ printf("booth <type> <operation> [options]\n");
+ printf("\n");
+ printf("Types:\n");
+ printf(" arbitrator: daemon running on arbitrator\n");
+ printf(" site: daemon running on cluster site\n");
+ printf(" client: command running from client\n");
+ printf("\n");
+ printf("Operations:\n");
+ printf("Please note that operations are valid iff type is client!\n");
+ printf("list: List all the tickets\n");
+ printf("grant: Grant ticket T(-t T) to site S(-s S)\n");
+ printf("revoke: Revoke ticket T(-t T) from local site\n");
+ printf("\n");
+ printf("Options:\n");
+ printf(" -D Enable debugging to stderr and don't fork\n");
+ printf(" -t ticket name\n");
+ printf(" -s site name\n");
+ printf(" -f ticket attribute: force, only valid when "
+ "granting\n");
+ printf(" -e ticket will failover after the expiry time if "
+ "not being renewed,\n\t\tset it while "
+ "granting. default: 600sec\n");
+ printf(" -h Print this help, then exit\n");
+}
+
+#define OPTION_STRING "Dt:s:fe:h"
+
+static int read_arguments(int argc, char **argv)
+{
+ int optchar;
+ char *arg1 = argv[1];
+ char *op;
+
+ if (argc < 2 || !strcmp(arg1, "help") || !strcmp(arg1, "--help") ||
+ !strcmp(arg1, "-h")) {
+ print_usage();
+ exit(EXIT_SUCCESS);
+ }
+
+ if (!strcmp(arg1, "version") || !strcmp(arg1, "--version") ||
+ !strcmp(arg1, "-V")) {
+ printf("%s %s (built %s %s)\n",
+ argv[0], RELEASE_VERSION, __DATE__, __TIME__);
+ exit(EXIT_SUCCESS);
+ }
+
+ if (!strcmp(arg1, "arbitrator")) {
+ cl.type = ACT_ARBITRATOR;
+ optind = 2;
+ } else if (!strcmp(arg1, "site")) {
+ cl.type = ACT_SITE;
+ optind = 2;
+ } else if (!strcmp(arg1, "client")) {
+ cl.type = ACT_CLIENT;
+ if (argc < 3) {
+ print_usage();
+ exit(EXIT_FAILURE);
+ }
+ op = argv[2];
+ optind = 3;
+ } else {
+ cl.type = ACT_CLIENT;
+ op = argv[1];
+ optind = 2;
+ }
+
+ switch (cl.type) {
+ case ACT_ARBITRATOR:
+ break;
+
+ case ACT_SITE:
+ break;
+
+ case ACT_CLIENT:
+ if (!strcmp(op, "list"))
+ cl.op = OP_LIST;
+ else if (!strcmp(op, "grant"))
+ cl.op = OP_GRANT;
+ else if (!strcmp(op, "revoke"))
+ cl.op = OP_REVOKE;
+ else {
+ fprintf(stderr, "client operation \"%s\" is unknown\n",
+ op);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ }
+
+ while (optind < argc) {
+ optchar = getopt(argc, argv, OPTION_STRING);
+
+ switch (optchar) {
+ case 'D':
+ cl.debug = 1;
+ log_logfile_priority = LOG_DEBUG;
+ log_syslog_priority = LOG_DEBUG;
+ break;
+
+ case 't':
+ if (cl.op == OP_GRANT || cl.op == OP_REVOKE)
+ strcpy(cl.ticket, optarg);
+ else {
+ print_usage();
+ exit(EXIT_FAILURE);
+ }
+ break;
+
+ case 's':
+ if (cl.op == OP_GRANT || cl.op == OP_REVOKE)
+ strcpy(cl.site, optarg);
+ else {
+ print_usage();
+ exit(EXIT_FAILURE);
+ }
+ break;
+
+ case 'f':
+ if (cl.op == OP_GRANT)
+ cl.force = 1;
+ else {
+ print_usage();
+ exit(EXIT_FAILURE);
+ }
+ break;
+
+ case 'e':
+ if (cl.op == OP_GRANT)
+ cl.expiry = atoi(optarg);
+ else {
+ print_usage();
+ exit(EXIT_FAILURE);
+ }
+ break;
+
+ case 'h':
+ print_usage();
+ exit(EXIT_SUCCESS);
+ break;
+
+ case ':':
+ case '?':
+ fprintf(stderr, "Please use '-h' for usage.\n");
+ exit(EXIT_FAILURE);
+ break;
+
+ default:
+ fprintf(stderr, "unknown option: %c\n", optchar);
+ exit(EXIT_FAILURE);
+ break;
+ };
+ }
+
+ return 0;
+}
+
+static void set_scheduler(void)
+{
+ struct sched_param sched_param;
+ struct rlimit rlimit;
+ int rv;
+
+ rlimit.rlim_cur = RLIM_INFINITY;
+ rlimit.rlim_max = RLIM_INFINITY;
+ setrlimit(RLIMIT_MEMLOCK, &rlimit);
+ rv = mlockall(MCL_CURRENT | MCL_FUTURE);
+ if (rv < 0) {
+ log_error("mlockall failed");
+ }
+
+ rv = sched_get_priority_max(SCHED_RR);
+ if (rv != -1) {
+ sched_param.sched_priority = rv;
+ rv = sched_setscheduler(0, SCHED_RR, &sched_param);
+ if (rv == -1)
+ log_error("could not set SCHED_RR priority %d err %d",
+ sched_param.sched_priority, errno);
+ } else {
+ log_error("could not get maximum scheduler priority err %d",
+ errno);
+ }
+}
+
+static void set_oom_adj(int val)
+{
+ FILE *fp;
+
+ fp = fopen("/proc/self/oom_adj", "w");
+ if (!fp)
+ return;
+
+ fprintf(fp, "%i", val);
+ fclose(fp);
+}
+
+static int do_arbitrator(void)
+{
+ int fd;
+
+ if (!cl.debug) {
+ if (daemon(0, 0) < 0) {
+ perror("daemon error");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ setup_logging();
+ fd = lockfile();
+ if (fd < 0)
+ return fd;
+
+ log_info("BOOTH arbitrator daemon started");
+ set_scheduler();
+ set_oom_adj(-16);
+
+ loop(ARBITRATOR);
+
+ unlink_lockfile(fd);
+ close_logging();
+
+ return 0;
+}
+
+static int do_site(void)
+{
+ int fd;
+
+ if (!cl.debug) {
+ if (daemon(0, 0) < 0) {
+ perror("daemon error");
<