📄 amandad.c
字号:
/* * Amanda, The Advanced Maryland Automatic Network Disk Archiver * Copyright (c) 1991-1999 University of Maryland at College Park * All Rights Reserved. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of U.M. not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. U.M. makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M. * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Authors: the Amanda Development Team. Its members are listed in a * file named AUTHORS, in the root directory of this distribution. *//* * $Id: amandad.c,v 1.18 2006/08/21 20:17:09 martinea Exp $ * * handle client-host side of Amanda network communications, including * security checks, execution of the proper service, and acking the * master side */#include "amanda.h"#include "amandad.h"#include "clock.h"#include "event.h"#include "amfeatures.h"#include "packet.h"#include "version.h"#include "queue.h"#include "security.h"#include "stream.h"#include "util.h"#include "conffile.h"#define REP_TIMEOUT (6*60*60) /* secs for service to reply */#define ACK_TIMEOUT 10 /* XXX should be configurable */#define amandad_debug(i, ...) do { \ if ((i) <= debug_amandad) { \ dbprintf(__VA_ARGS__); \ } \} while (0)/* * These are the actions for entering the state machine */typedef enum { A_START, A_RECVPKT, A_RECVREP, A_PENDING, A_FINISH, A_CONTINUE, A_SENDNAK, A_TIMEOUT } action_t;/* * This is a state in the state machine. It is a function pointer to * the function that actually implements the state. */struct active_service;typedef action_t (*state_t)(struct active_service *, action_t, pkt_t *);/* * This structure describes an active running service. * * An active service is something running that we have received * a request for. This structure holds info on that service, including * file descriptors for data, etc, as well as the security handle * for communications with the amanda server. */struct active_service { char *cmd; /* name of command we ran */ char *arguments; /* arguments we sent it */ security_handle_t *security_handle; /* remote server */ state_t state; /* how far this has progressed */ pid_t pid; /* pid of subprocess */ int send_partial_reply; /* send PREP packet */ int reqfd; /* pipe to write requests */ int repfd; /* pipe to read replies */ event_handle_t *ev_repfd; /* read event handle for repfd */ event_handle_t *ev_reptimeout; /* timeout for rep data */ pkt_t rep_pkt; /* rep packet we're sending out */ char *repbuf; /* buffer to read the rep into */ size_t bufsize; /* length of repbuf */ size_t repbufsize; /* length of repbuf */ int repretry; /* times we'll retry sending the rep */ /* * General user streams to the process, and their equivalent * network streams. */ struct datafd_handle { int fd_read; /* pipe to child process */ int fd_write; /* pipe to child process */ event_handle_t *ev_read; /* it's read event handle */ event_handle_t *ev_write; /* it's write event handle */ security_stream_t *netfd; /* stream to amanda server */ struct active_service *as; /* pointer back to our enclosure */ } data[DATA_FD_COUNT]; char databuf[NETWORK_BLOCK_BYTES]; /* buffer to relay netfd data in */ TAILQ_ENTRY(active_service) tq; /* queue handle */};/* * Here are the services that we allow. */static struct services { char *name; int active;} services[] = { { "noop", 1 }, { "sendsize", 1 }, { "sendbackup", 1 }, { "selfcheck", 1 }, { "amindexd", 0 }, { "amidxtaped", 0 }};#define NSERVICES (int)(sizeof(services) / sizeof(services[0]))/* * Queue of outstanding requests that we are running. */static struct { TAILQ_HEAD(, active_service) tailq; int qlength;} serviceq = { TAILQ_HEAD_INITIALIZER(serviceq.tailq), 0};static int wait_30s = 1;static int exit_on_qlength = 1;static char *auth = NULL;static kencrypt_type amandad_kencrypt = KENCRYPT_NONE;int main(int argc, char **argv);static int allocstream(struct active_service *, int);static void exit_check(void *);static void protocol_accept(security_handle_t *, pkt_t *);static void state_machine(struct active_service *, action_t, pkt_t *);static action_t s_sendack(struct active_service *, action_t, pkt_t *);static action_t s_repwait(struct active_service *, action_t, pkt_t *);static action_t s_processrep(struct active_service *, action_t, pkt_t *);static action_t s_sendrep(struct active_service *, action_t, pkt_t *);static action_t s_ackwait(struct active_service *, action_t, pkt_t *);static void repfd_recv(void *);static void timeout_repfd(void *);static void protocol_recv(void *, pkt_t *, security_status_t);static void process_readnetfd(void *);static void process_writenetfd(void *, void *, ssize_t);static struct active_service *service_new(security_handle_t *, const char *, const char *);static void service_delete(struct active_service *);static int writebuf(struct active_service *, const void *, size_t);static ssize_t do_sendpkt(security_handle_t *handle, pkt_t *pkt);static char *amandad_get_security_conf (char *, void *);static const char *state2str(state_t);static const char *action2str(action_t);intmain( int argc, char ** argv){ int i, j; int have_services; int in, out; const security_driver_t *secdrv; int no_exit = 0; char *pgm = "amandad"; /* in case argv[0] is not set */#if defined(USE_REUSEADDR) const int on = 1; int r;#endif /* * Configure program for internationalization: * 1) Only set the message locale for now. * 2) Set textdomain for all amanda related programs to "amanda" * We don't want to be forced to support dozens of message catalogs. */ setlocale(LC_MESSAGES, "C"); textdomain("amanda"); safe_fd(-1, 0); safe_cd(); /* * When called via inetd, it is not uncommon to forget to put the * argv[0] value on the config line. On some systems (e.g. Solaris) * this causes argv and/or argv[0] to be NULL, so we have to be * careful getting our name. */ if ((argv == NULL) || (argv[0] == NULL)) { pgm = "amandad"; /* in case argv[0] is not set */ } else { pgm = basename(argv[0]); /* Strip of leading path get debug name */ } set_pname(pgm); dbopen(DBG_SUBDIR_AMANDAD); if(argv == NULL) { error(_("argv == NULL\n")); /*NOTREACHED*/ } /* Don't die when child closes pipe */ signal(SIGPIPE, SIG_IGN); config_init(CONFIG_INIT_CLIENT, NULL); check_running_as(RUNNING_AS_CLIENT_LOGIN); erroutput_type = (ERR_INTERACTIVE|ERR_SYSLOG); /* * ad-hoc argument parsing * * We accept -auth=[authentication type] * -no-exit * -tcp=[port] * -udp=[port] * We also add a list of services that amandad can launch */ secdrv = NULL; in = 0; out = 1; /* default to stdin/stdout */ have_services = 0; for (i = 1; i < argc; i++) { /* * accept -krb4 as an alias for -auth=krb4 (for compatibility) */ if (strcmp(argv[i], "-krb4") == 0) { argv[i] = "-auth=krb4"; /* FALLTHROUGH */ auth = "krb4"; } /* * Get a driver for a security type specified after -auth= */ else if (strncmp(argv[i], "-auth=", strlen("-auth=")) == 0) { argv[i] += strlen("-auth="); secdrv = security_getdriver(argv[i]); auth = argv[i]; if (secdrv == NULL) { error(_("no driver for security type '%s'\n"), argv[i]); /*NOTREACHED*/ } continue; } /* * If -no-exit is specified, always run even after requests have * been satisfied. */ else if (strcmp(argv[i], "-no-exit") == 0) { no_exit = 1; continue; } /* * Allow us to directly bind to a udp port for debugging. * This may only apply to some security types. */ else if (strncmp(argv[i], "-udp=", strlen("-udp=")) == 0) {#ifdef WORKING_IPV6 struct sockaddr_in6 sin;#else struct sockaddr_in sin;#endif argv[i] += strlen("-udp=");#ifdef WORKING_IPV6 in = out = socket(AF_INET6, SOCK_DGRAM, 0);#else in = out = socket(AF_INET, SOCK_DGRAM, 0);#endif if (in < 0) { error(_("can't create dgram socket: %s\n"), strerror(errno)); /*NOTREACHED*/ }#ifdef USE_REUSEADDR r = setsockopt(in, SOL_SOCKET, SO_REUSEADDR, (void *)&on, (socklen_t)sizeof(on)); if (r < 0) { dbprintf(_("amandad: setsockopt(SO_REUSEADDR) failed: %s\n"), strerror(errno)); }#endif#ifdef WORKING_IPV6 sin.sin6_family = (sa_family_t)AF_INET6; sin.sin6_addr = in6addr_any; sin.sin6_port = (in_port_t)htons((in_port_t)atoi(argv[i]));#else sin.sin_family = (sa_family_t)AF_INET; sin.sin_addr.s_addr = INADDR_ANY; sin.sin_port = (in_port_t)htons((in_port_t)atoi(argv[i]));#endif if (bind(in, (struct sockaddr *)&sin, (socklen_t)sizeof(sin)) < 0) { error(_("can't bind to port %d: %s\n"), atoi(argv[i]), strerror(errno)); /*NOTREACHED*/ } } /* * Ditto for tcp ports. */ else if (strncmp(argv[i], "-tcp=", strlen("-tcp=")) == 0) {#ifdef WORKING_IPV6 struct sockaddr_in6 sin;#else struct sockaddr_in sin;#endif int sock; socklen_t n; argv[i] += strlen("-tcp=");#ifdef WORKING_IPV6 sock = socket(AF_INET6, SOCK_STREAM, 0);#else sock = socket(AF_INET, SOCK_STREAM, 0);#endif if (sock < 0) { error(_("can't create tcp socket: %s\n"), strerror(errno)); /*NOTREACHED*/ }#ifdef USE_REUSEADDR r = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, (socklen_t)sizeof(on)); if (r < 0) { dbprintf(_("amandad: setsockopt(SO_REUSEADDR) failed: %s\n"), strerror(errno)); }#endif#ifdef WORKING_IPV6 sin.sin6_family = (sa_family_t)AF_INET6; sin.sin6_addr = in6addr_any; sin.sin6_port = (in_port_t)htons((in_port_t)atoi(argv[i]));#else sin.sin_family = (sa_family_t)AF_INET; sin.sin_addr.s_addr = INADDR_ANY; sin.sin_port = (in_port_t)htons((in_port_t)atoi(argv[i]));#endif if (bind(sock, (struct sockaddr *)&sin, (socklen_t)sizeof(sin)) < 0) { error(_("can't bind to port %d: %s\n"), atoi(argv[i]), strerror(errno)); /*NOTREACHED*/ } listen(sock, 10); n = (socklen_t)sizeof(sin); in = out = accept(sock, (struct sockaddr *)&sin, &n); } /* * It must be a service name */ else { /* clear all services */ if(!have_services) { for (j = 0; j < (int)NSERVICES; j++) services[j].active = 0; } have_services = 1; if(strcmp(argv[i],"amdump") == 0) { services[0].active = 1; services[1].active = 1; services[2].active = 1; services[3].active = 1; } else { for (j = 0; j < (int)NSERVICES; j++) if (strcmp(services[j].name, argv[i]) == 0) break; if (j == (int)NSERVICES) { dbprintf(_("%s: invalid service\n"), argv[i]); exit(1); } services[j].active = 1; } } } /* * If no security type specified, use BSD */ if (secdrv == NULL) { secdrv = security_getdriver("BSD"); auth = "bsd"; if (secdrv == NULL) { error(_("no driver for default security type 'BSD'\n")); /*NOTREACHED*/ } } if(strcasecmp(auth, "rsh") == 0 || strcasecmp(auth, "ssh") == 0 || strcasecmp(auth, "bsdtcp") == 0) { wait_30s = 0; exit_on_qlength = 1; } /* initialize */ startclock(); dbprintf(_("version %s\n"), version()); for (i = 0; version_info[i] != NULL; i++) { dbprintf(" %s", version_info[i]); } if (! (argc >= 1 && argv != NULL && argv[0] != NULL)) { dbprintf(_("WARNING: argv[0] not defined: check inetd.conf\n")); } /* * Schedule to call protocol_accept() when new security handles * are created on stdin. */ security_accept(secdrv, amandad_get_security_conf, in, out, protocol_accept, NULL); /* * Schedule an event that will try to exit every 30 seconds if there * are no requests outstanding. */ if(wait_30s) (void)event_register((event_id_t)30, EV_TIME, exit_check, &no_exit); /* * Call event_loop() with an arg of 0, telling it to block until all * events are completed. */ event_loop(0); close(in); close(out); dbclose(); return(0);}/* * This runs periodically and checks to see if we have any active services * still running. If we don't, then we quit. */static voidexit_check( void * cookie){ int no_exit; assert(cookie != NULL); no_exit = *(int *)cookie; /* * If things are still running, then don't exit. */ if (serviceq.qlength > 0) return; /* * If the caller asked us to never exit, then we're done */ if (no_exit) return; dbclose(); exit(0);}/* * Handles new incoming protocol handles. This is a callback for * security_accept(), which gets called when new handles are detected. */static voidprotocol_accept( security_handle_t * handle, pkt_t * pkt){ pkt_t pkt_out; struct active_service *as; char *pktbody, *tok, *service, *arguments; char *service_path = NULL; int i; pkt_out.body = NULL; /* * If handle is NULL, then the connection is closed. */ if(handle == NULL) { return; } /* * If pkt is NULL, then there was a problem with the new connection. */ if (pkt == NULL) { dbprintf(_("accept error: %s\n"), security_geterror(handle)); pkt_init(&pkt_out, P_NAK, "ERROR %s\n", security_geterror(handle)); do_sendpkt(handle, &pkt_out); amfree(pkt_out.body); security_close(handle); return; } dbprintf(_("accept recv %s pkt:\n<<<<<\n%s>>>>>\n"), pkt_type2str(pkt->type), pkt->body); /* * If this is not a REQ packet, just forget about it. */ if (pkt->type != P_REQ) { dbprintf(_("received unexpected %s packet:\n<<<<<\n%s>>>>>\n\n"), pkt_type2str(pkt->type), pkt->body); security_close(handle); return; } pktbody = service = arguments = NULL; as = NULL; /* * Parse out the service and arguments */ pktbody = stralloc(pkt->body); tok = strtok(pktbody, " "); if (tok == NULL) goto badreq; if (strcmp(tok, "SERVICE") != 0) goto badreq; tok = strtok(NULL, " \n"); if (tok == NULL) goto badreq; service = stralloc(tok); /* we call everything else 'arguments' */ tok = strtok(NULL, ""); if (tok == NULL) goto badreq; arguments = stralloc(tok);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -