All new accounts created on Gitlab now require administrator approval. If you invite any collaborators, please let Flux staff know so they can approve the accounts.

Commit a2a896ab authored by Mike Hibler's avatar Mike Hibler

First crack at a frisbee "master server" for handling GET (download) requests.

There are a couple of new packet types in the frisbee protocol which are
exchanged via TCP with the master server: GETREQUEST and GETREPLY.  The
client passes to the master server an opaque imageid and a couple of options
and gets back the addr/port to use to actually download the image.  The
implementation of the master server is fragile and is more of a test
framework, Grant is working on a more robust master server.  I am mostly
doing a backend that communicates with the Emulab DB to do its authentication
and making the client changes.

The client now uses the -S option to specify the IP address of the master
server and the -F option to specify an imageid.  If no error is returned,
the image is downloaded using the returned addr/port.  If -Q is used in place
of -F, then the client makes a "status only" call getting back info about
whether the named image is accessible to the client and whether a server is
currently running.

On the server side, the new master server (mserver.c) has an Emulab
configuration "backend" that supports host-based authentication.
The IP address of the caller is mapped to a node_id/pid/gid/eid combo
that is used to determine access.  On a request, the specified imageid is
treated either as a pathname (if it starts with '/') or an image identifier
of the form "<pid>/<imagename>".  If it is a pathname, we check to make
sure that pathname (after running through "realpath") is contained in one
of the directories accessible to that node in its current experiment context;
i.e., /share, /proj/<pid>, /groups/<pid>/<gid>, or /users/<swapper-uid>.
If it is an image identifier, the DB is queried to ensure that access is
allowed to that image; i.e., it must be "global" or in the appropriate
project/group.

The master server forks a frisbeed for each valid request, if one is not
already running.  The multicast address selection is still based on the
emulab_indicies.frisbee_index field, but the address/port/server info is no
longer stored in the frisbee_blobs table (frisbee_pid, load_address,
load_busy are not set).

Note that this is not yet integrated in the os_load path.  Further work is
required to replace frisbeelauncher.
parent b1e4b205
......@@ -46,6 +46,8 @@
#undef TPM
#undef NOSHAREDFS
#undef NFSRACY
#undef FRISEBEEMCASTADDR
#undef FRISEBEEMCASTPORT
#undef HAVE_SRANDOMDEV
......
......@@ -1784,6 +1784,14 @@ cat >> confdefs.h <<EOF
#define BOSSEVENTPORT "$BOSSEVENTPORT"
EOF
cat >> confdefs.h <<EOF
#define FRISEBEEMCASTADDR "$FRISEBEEMCASTADDR"
EOF
cat >> confdefs.h <<EOF
#define FRISEBEEMCASTPORT "$FRISEBEEMCASTPORT"
EOF
if test $OPSDBSUPPORT -eq 1; then
cat >> confdefs.h <<EOF
......@@ -2222,17 +2230,17 @@ for ac_hdr in ulxmlrpcpp/ulxr_config.h
do
ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
echo "configure:2225: checking for $ac_hdr" >&5
echo "configure:2234: checking for $ac_hdr" >&5
if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
cat > conftest.$ac_ext <<EOF
#line 2230 "configure"
#line 2239 "configure"
#include "confdefs.h"
#include <$ac_hdr>
EOF
ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
{ (eval echo configure:2235: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
{ (eval echo configure:2244: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
if test -z "$ac_err"; then
rm -rf conftest*
......@@ -2271,17 +2279,17 @@ for ac_hdr in linux/videodev.h
do
ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
echo "configure:2274: checking for $ac_hdr" >&5
echo "configure:2283: checking for $ac_hdr" >&5
if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
cat > conftest.$ac_ext <<EOF
#line 2279 "configure"
#line 2288 "configure"
#include "confdefs.h"
#include <$ac_hdr>
EOF
ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
{ (eval echo configure:2284: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
{ (eval echo configure:2293: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
if test -z "$ac_err"; then
rm -rf conftest*
......@@ -2314,7 +2322,7 @@ done
# Extract the first word of "gtk-config", so it can be a program name with args.
set dummy gtk-config; ac_word=$2
echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
echo "configure:2317: checking for $ac_word" >&5
echo "configure:2326: checking for $ac_word" >&5
if eval "test \"`echo '$''{'ac_cv_prog_GTK_CONFIG'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
......@@ -2371,7 +2379,7 @@ ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftes
cross_compiling=$ac_cv_prog_cxx_cross
echo $ac_n "checking how to run the C++ preprocessor""... $ac_c" 1>&6
echo "configure:2374: checking how to run the C++ preprocessor" >&5
echo "configure:2383: checking how to run the C++ preprocessor" >&5
if test -z "$CXXCPP"; then
if eval "test \"`echo '$''{'ac_cv_prog_CXXCPP'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
......@@ -2384,12 +2392,12 @@ ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftes
cross_compiling=$ac_cv_prog_cxx_cross
CXXCPP="${CXX-g++} -E"
cat > conftest.$ac_ext <<EOF
#line 2387 "configure"
#line 2396 "configure"
#include "confdefs.h"
#include <stdlib.h>
EOF
ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
{ (eval echo configure:2392: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
{ (eval echo configure:2401: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
if test -z "$ac_err"; then
:
......@@ -2415,17 +2423,17 @@ echo "$ac_t""$CXXCPP" 1>&6
ac_safe=`echo "xercesc/dom/DOM.hpp" | sed 'y%./+-%__p_%'`
echo $ac_n "checking for xercesc/dom/DOM.hpp""... $ac_c" 1>&6
echo "configure:2418: checking for xercesc/dom/DOM.hpp" >&5
echo "configure:2427: checking for xercesc/dom/DOM.hpp" >&5
if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
cat > conftest.$ac_ext <<EOF
#line 2423 "configure"
#line 2432 "configure"
#include "confdefs.h"
#include <xercesc/dom/DOM.hpp>
EOF
ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
{ (eval echo configure:2428: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
{ (eval echo configure:2437: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
if test -z "$ac_err"; then
rm -rf conftest*
......@@ -2489,7 +2497,7 @@ fi
# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
# ./install, which can be erroneously created by make from ./install.sh.
echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6
echo "configure:2492: checking for a BSD compatible install" >&5
echo "configure:2501: checking for a BSD compatible install" >&5
if test -z "$INSTALL"; then
if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
......@@ -2550,7 +2558,7 @@ esac
# Extract the first word of "rsync", so it can be a program name with args.
set dummy rsync; ac_word=$2
echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
echo "configure:2553: checking for $ac_word" >&5
echo "configure:2562: checking for $ac_word" >&5
if eval "test \"`echo '$''{'ac_cv_path_RSYNC'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
......
......@@ -406,6 +406,8 @@ if test -n "$FSDIR_SCRATCH"; then
AC_DEFINE_UNQUOTED(FSDIR_SCRATCH, "$FSDIR_SCRATCH")
fi
AC_DEFINE_UNQUOTED(BOSSEVENTPORT, "$BOSSEVENTPORT")
AC_DEFINE_UNQUOTED(FRISEBEEMCASTADDR, "$FRISEBEEMCASTADDR")
AC_DEFINE_UNQUOTED(FRISEBEEMCASTPORT, "$FRISEBEEMCASTPORT")
if test $OPSDBSUPPORT -eq 1; then
AC_DEFINE_UNQUOTED(OPSDBSUPPORT, 1)
......
......@@ -175,6 +175,12 @@ mydb_update(char *query, ...)
return 1;
}
int
mydb_insertid()
{
return (int)mysql_insert_id(&db);
}
/*
* Map IP to node ID.
*/
......
......@@ -33,6 +33,7 @@ int mydb_seteventschedulerpid(char *pid, char *eid, int processid);
*/
MYSQL_RES *mydb_query(char *query, int ncols, ...);
int mydb_update(char *query, ...);
int mydb_insertid(void);
unsigned long mydb_escape_string(char *to, const char *from,
unsigned long length);
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2009 University of Utah and the Flux Group.
# Copyright (c) 2000-2010 University of Utah and the Flux Group.
# All rights reserved.
#
......@@ -63,7 +63,13 @@ SERVERFLAGS = $(CFLAGS)
SERVERLIBS = $(PTHREADLIBS)
SERVEROBJS = server.o $(SHAREDOBJS)
CFLAGS = -O2 -g -Wall -fno-builtin-log $(LDSTATIC) $(PTHREADCFLAGS) -DSTATS
MYSQLCFLAGS = -I/usr/local/include
MYSQLLIBS = -L/usr/local/lib/mysql -lmysqlclient
MSERVERFLAGS = $(CFLAGS) $(MYSQLCFLAGS) -DUSE_EMULAB_CONFIG -I$(TESTBED_SRCDIR)/lib/libtb -I$(OBJDIR)
MSERVERLIBS = $(OBJDIR)/lib/libtb/libtb.a $(MYSQLLIBS) $(PTHREADLIBS)
MSERVEROBJS = mserver.o config.o config_emulab.o $(SHAREDOBJS)
CFLAGS = -O2 -g -Wall -fno-builtin-log $(LDSTATIC) $(PTHREADCFLAGS) -DSTATS -DMASTER_SERVER
LDFLAGS = $(LDSTATIC)
#
......@@ -110,6 +116,15 @@ frisbeed: $(SERVEROBJS)
cp frisbeed frisbeed.debug
strip frisbeed
mfrisbeed: $(MSERVEROBJS)
$(CC) $(LDFLAGS) $(MSERVERFLAGS) $(MSERVEROBJS) $(MSERVERLIBS) -o mfrisbeed
cp mfrisbeed mfrisbeed.debug
strip mfrisbeed
config.o: $(SRCDIR)/config.c configdefs.h log.h
$(CC) -c $(MSERVERFLAGS) $(SRCDIR)/config.c
config_emulab.o: $(SRCDIR)/config_emulab.c configdefs.h log.h
$(CC) -c $(MSERVERFLAGS) $(SRCDIR)/config_emulab.c
log.o: $(SRCDIR)/log.c decls.h log.h
$(CC) $(CFLAGS) -DLOG_TESTBED=$(LOG_TESTBED) -c $(SRCDIR)/log.c
......
......@@ -60,6 +60,8 @@ int zero = 0;
int portnum;
struct in_addr mcastaddr;
struct in_addr mcastif;
char *imageid;
int askonly;
static struct timeval stamp;
static struct in_addr serverip;
......@@ -137,18 +139,28 @@ ClientStats_t Stats;
#endif
char *usagestr =
"usage: frisbee [-drzbn] [-s #] <-p #> <-m ipaddr> <output filename>\n"
"usage: frisbee [-drzbnN] [-s #] <-m ipaddr> <-p #> <output filename>\n"
" or\n"
"usage: frisbee [-drzbnN] [-s #] <-S server> <-F fileid> <output filename>\n"
"\n"
" -d Turn on debugging. Multiple -d options increase output.\n"
" -r Randomly delay first request by up to one second.\n"
" -z Zero fill unused block ranges (default is to seek past).\n"
" -b Use broadcast instead of multicast\n"
" -n Do not use extra threads in diskwriter\n"
" -N Do not decompress the received data, just write to output.\n"
" -S server-IP Specify the IP address of the server to use.\n"
" -p portnum Specify a port number.\n"
" -m mcastaddr Specify a multicast address in dotted notation.\n"
" -i mcastif Specify a multicast interface in dotted notation.\n"
" -s slice Output to DOS slice (DOS numbering 1-4)\n"
" NOTE: Must specify a raw disk device for output filename.\n"
" -D DOS-type With -s, sets the DOS type of the slice in the MBR\n"
" -F file-ID Specify the ID of the file (image) to download.\n"
" Here -S specifies the 'master' server which will\n"
" return unicast/multicast info to use for image download.\n"
" -Q file-ID Ask the server (-S) about the indicated file (image).\n"
" Tells whether the image is accessible by this node/user.\n"
"\n"
"tuning options (if you don't know what they are, don't use em!):\n"
" -C MB Max MB of memory to use for network chunk buffering.\n"
......@@ -187,11 +199,11 @@ int
main(int argc, char **argv)
{
int ch, mem;
char *filename;
char *filename = NULL;
int dostype = -1;
int slice = 0;
while ((ch = getopt(argc, argv, "dhp:m:s:i:tbznT:r:E:D:C:W:S:M:R:I:ON")) != -1)
while ((ch = getopt(argc, argv, "dhp:m:s:i:tbznT:r:E:D:C:W:S:M:R:I:ONF:Q:")) != -1)
switch(ch) {
case 'd':
debug++;
......@@ -239,6 +251,15 @@ main(int argc, char **argv)
}
break;
case 'F':
imageid = optarg;
break;
case 'Q':
imageid = optarg;
askonly = 1;
break;
case 't':
tracing++;
break;
......@@ -314,14 +335,26 @@ main(int argc, char **argv)
argc -= optind;
argv += optind;
if (!askonly) {
if (argc != 1)
usage();
filename = argv[0];
}
if (!portnum || ! mcastaddr.s_addr)
if (!((imageid != NULL && serverip.s_addr != 0) ||
(mcastaddr.s_addr != 0 && portnum != 0)))
usage();
ClientLogInit();
#ifdef MASTER_SERVER
if (imageid && !ClientNetFindServer(&serverip, imageid, askonly, 5)) {
fprintf(stderr, "Could not get download info for '%s'\n",
imageid);
exit(1);
}
if (askonly)
exit(0);
#endif
ClientNetInit();
#ifdef DOEVENTS
......
/*
* Configuration file functions.
*/
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <assert.h>
#include "configdefs.h"
#include "log.h"
static struct config *myconfig;
/*
* Got a SIGHUP.
* Reread the config file.
*/
static void
config_signal(int sig)
{
void *savedconfig = NULL;
if (myconfig == NULL)
return;
savedconfig = myconfig->config_save();
if (myconfig->config_read() != 0) {
warning("WARNING: could not load new configuration, "
"restoring old config.");
if (savedconfig == NULL ||
myconfig->config_restore(savedconfig)) {
error("*** Could not restore old configuration; "
"aborting!");
abort();
}
} else
log("New configuration loaded.");
if (savedconfig)
myconfig->config_free(savedconfig);
}
int
config_init(int readit)
{
int rv;
#ifdef USE_EMULAB_CONFIG
extern struct config emulab_config;
myconfig = &emulab_config;
#else
extern struct config file_config;
myconfig = &file_config;
#endif
rv = myconfig->config_init();
if (rv)
return rv;
if (readit) {
rv = myconfig->config_read();
if (rv) {
myconfig->config_deinit();
return rv;
}
}
signal(SIGHUP, config_signal);
return 0;
}
int
config_read(void)
{
assert(myconfig != NULL);
return myconfig->config_read();
}
int
config_get_host_authinfo(struct in_addr *in, char *imageid,
struct config_host_authinfo **get,
struct config_host_authinfo **put)
{
assert(myconfig != NULL);
return myconfig->config_get_host_authinfo(in, imageid, get, put);
}
void
config_free_host_authinfo(struct config_host_authinfo *ai)
{
assert(myconfig != NULL);
return myconfig->config_free_host_authinfo(ai);
}
/*
* Authenticate access to an image based on the IP address of a host.
* Return zero and (optionally) a pointer to authentication information
* on success, an error code otherwise.
*/
int
config_auth_by_IP(struct in_addr *host, char *imageid,
struct config_host_authinfo **aip)
{
struct config_host_authinfo *ai;
if (config_get_host_authinfo(host, imageid, &ai, 0))
return CONFIG_ERR_HA_FAILED;
if (ai->hostid == NULL) {
config_free_host_authinfo(ai);
return CONFIG_ERR_HA_NOHOST;
}
if (ai->numimages == 0) {
config_free_host_authinfo(ai);
return CONFIG_ERR_HA_NOACCESS;
}
if (aip)
*aip = ai;
return 0;
}
int
config_get_server_address(struct config_host_authinfo *ai, int methods,
in_addr_t *addr, in_port_t *port, int *method)
{
assert(myconfig != NULL);
return myconfig->config_get_server_address(ai, methods,
addr, port, method);
}
char *
config_perror(int code)
{
switch (code) {
case CONFIG_ERR_HA_FAILED:
return "host authentication failed";
case CONFIG_ERR_HA_NOHOST:
return "unknown host";
case CONFIG_ERR_HA_NOIMAGE:
return "unknown image";
case CONFIG_ERR_HA_NOACCESS:
return "permission denied";
case CONFIG_ERR_HA_NOMETHOD:
return "invalid method";
default:
return "unknown error";
}
}
void
config_dump(FILE *fd)
{
signal(SIGHUP, SIG_IGN);
if (myconfig == NULL)
warning("config_dump: config file not yet read.");
else
myconfig->config_dump(fd);
signal(SIGHUP, config_signal);
}
This diff is collapsed.
#include <netinet/in.h>
#include <arpa/inet.h>
/*
* Config info for a single image
* XXX needs to be extended for REs.
*/
struct config_imageinfo {
char *imageid; /* unique name of image */
char *path; /* path where image is stored */
int flags; /* */
char *get_options; /* options for GET operation */
int get_methods; /* allowed GET transfer mechanisms */
char *put_options; /* options for PUT operation */
void *extra; /* config-type specific info */
};
/* flags */
#define CONFIG_IMAGE_ISFILE 0x1 /* path is an image file */
#define CONFIG_IMAGE_ISDIR 0x2 /* path is a directory */
#define CONFIG_IMAGE_ISGLOB 0x4 /* path is a file glob */
#define CONFIG_IMAGE_ISRE 0x8 /* path is a perl RE */
/* methods */
#define CONFIG_IMAGE_UNKNOWN 0x0
#define CONFIG_IMAGE_UCAST 0x1
#define CONFIG_IMAGE_MCAST 0x2
#define CONFIG_IMAGE_BCAST 0x4
struct config_host_authinfo {
char *hostid; /* unique name of host */
int numimages; /* number of images in info array */
struct config_imageinfo *imageinfo; /* info array */
void *extra; /* config-type specific info */
};
/*
* Config file functions
*/
struct config {
int (*config_init)(void);
void (*config_deinit)(void);
int (*config_read)(void);
int (*config_get_host_authinfo)(struct in_addr *, char *,
struct config_host_authinfo **,
struct config_host_authinfo **);
void (*config_free_host_authinfo)(struct config_host_authinfo *);
int (*config_get_server_address)(struct config_host_authinfo *, int,
in_addr_t *, in_port_t *, int *);
void *(*config_save)(void);
int (*config_restore)(void *);
void (*config_free)(void *);
void (*config_dump)(FILE *);
};
extern int config_init(int);
extern void config_deinit(void);
extern int config_read(void);
extern int config_get_host_authinfo(struct in_addr *, char *,
struct config_host_authinfo **,
struct config_host_authinfo **);
extern void config_free_host_authinfo(struct config_host_authinfo *);
extern int config_auth_by_IP(struct in_addr *, char *,
struct config_host_authinfo **);
extern int config_get_server_address(struct config_host_authinfo *, int,
in_addr_t *, in_port_t *, int *);
extern char * config_perror(int);
extern void * config_save(void);
extern int config_restore(void *);
extern void config_dump(FILE *);
#define CONFIG_ERR_HA_FAILED 1 /* internal host auth error */
#define CONFIG_ERR_HA_NOHOST 2 /* no such host */
#define CONFIG_ERR_HA_NOIMAGE 3 /* no such image */
#define CONFIG_ERR_HA_NOACCESS 4 /* access not allowed for host */
#define CONFIG_ERR_HA_NOMETHOD 5 /* not avail to host via method */
......@@ -314,6 +314,50 @@ typedef struct {
#define PKTSUBTYPE_PREQUEST 6
#define PKTSUBTYPE_JOIN2 7
#ifdef MASTER_SERVER
#include <netinet/in.h>
/* default port number: 0xfbee */
#define MS_PORTNUM 64494
/* imageid length: large enough to hold an ascii encoded SHA 1024 hash */
#define MS_MAXIDLEN 256
/*
* Master server messages.
* These are sent via unicast TCP.
*/
typedef struct {
int32_t type;
union {
struct {
uint8_t methods;
uint8_t status;
uint16_t idlen;
char imageid[MS_MAXIDLEN];
} __attribute__((__packed__)) getrequest;
struct {
uint8_t method;
uint8_t isrunning;
uint16_t error;
in_addr_t addr;
in_port_t port;
} __attribute__((__packed__)) getreply;
} body;
} MasterMsg_t;
#define MS_MSGTYPE_GETREQUEST 1
#define MS_MSGTYPE_GETREPLY 2
#define MS_MSGTYPE_PUTREQUEST 3
#define MS_MSGTYPE_PUTREPLY 4
#define MS_METHOD_UNKNOWN 0
#define MS_METHOD_UNICAST 1
#define MS_METHOD_MULTICAST 2
#define MS_METHOD_BROADCAST 4
#define MS_METHOD_ANY 7
#endif
/*
* Protos.
*/
......@@ -327,6 +371,11 @@ void PacketSend(Packet_t *p, int *resends);
void PacketReply(Packet_t *p);
int PacketValid(Packet_t *p, int nchunks);
void dump_network(void);
#ifdef MASTER_SERVER
int ClientNetFindServer(struct in_addr *, char *, int, int);
int MsgSend(int, MasterMsg_t *, size_t, int);
int MsgReceive(int, MasterMsg_t *, size_t, int);
#endif
/*
* Globals
......
/*
* EMULAB-COPYRIGHT
* Copyright (c) 2000-2003 University of Utah and the Flux Group.
* Copyright (c) 2000-2010 University of Utah and the Flux Group.
* All rights reserved.
*/
......@@ -47,6 +47,37 @@ ServerLogInit(void)
return 0;
}
int
MasterServerLogInit(void)
{
if (debug) {
usesyslog = 0;
return 1;
}
openlog("mfrisbeed", LOG_PID, LOG_TESTBED);
return 0;
}
void
info(const char *fmt, ...)
{
va_list args;
char buf[BUFSIZ];
va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
if (!usesyslog) {
fputs(buf, stderr);
fputc('\n', stderr);
}
else
syslog(LOG_INFO, "%s", buf);
}
void
log(const char *fmt, ...)
{
......@@ -90,6 +121,7 @@ error(const char *fmt, ...)
va_start(args, fmt);
if (!usesyslog) {
vfprintf(stderr, fmt, args);
fputc('\n', stderr);
fflush(stderr);
}
else
......