/* $OpenBSD: conf.y,v 1.11 2006/06/02 20:31:48 moritz Exp $ */ /* * Copyright (c) 2005 H�kan Olsson. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* Definitions */ %{ #include <sys/types.h> #include <sys/stat.h> #include <sys/socket.h> #include <ctype.h> #include <fcntl.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <pwd.h> #include "sasyncd.h" #include "net.h" /* Global configuration context. */ struct cfgstate cfgstate; /* Local variables */ int conflen = 0; char *confbuf, *confptr; int yyparse(void); int yylex(void); void yyerror(const char *); %} %union { char *string; int val; } %token MODE INTERFACE INTERVAL LISTEN ON PORT PEER SHAREDKEY %token Y_SLAVE Y_MASTER INET INET6 FLUSHMODE STARTUP NEVER SYNC %token GROUP SKIPSLAVE %token <string> STRING %token <val> VALUE %type <val> af port mode flushmode %% /* Rules */ settings : /* empty */ | settings setting ; af : /* empty */ { $$ = AF_UNSPEC; } | INET { $$ = AF_INET; } | INET6 { $$ = AF_INET6; } ; port : /* empty */ { $$ = SASYNCD_DEFAULT_PORT; } | PORT VALUE { $$ = $2; } ; mode : Y_MASTER { $$ = MASTER; } | Y_SLAVE { $$ = SLAVE; } ; modes : SKIPSLAVE { cfgstate.flags |= SKIP_LOCAL_SAS; log_msg(2, "config: not syncing SA to peers"); } | mode { const char *m[] = CARPSTATES; cfgstate.lockedstate = $1; log_msg(2, "config: mode set to %s", m[$1]); } ; flushmode : STARTUP { $$ = FM_STARTUP; } | NEVER { $$ = FM_NEVER; } | SYNC { $$ = FM_SYNC; } ; setting : INTERFACE STRING { if (cfgstate.carp_ifname) free(cfgstate.carp_ifname); cfgstate.carp_ifname = $2; log_msg(2, "config: interface %s", cfgstate.carp_ifname); } | GROUP STRING { if (cfgstate.carp_ifgroup) free(cfgstate.carp_ifgroup); cfgstate.carp_ifgroup = $2; log_msg(2, "config: group %s", cfgstate.carp_ifgroup); } | FLUSHMODE flushmode { const char *fm[] = { "STARTUP", "NEVER", "SYNC" }; cfgstate.flags |= $2; log_msg(2, "config: flush mode set to %s", fm[$2]); } | PEER STRING { struct syncpeer *peer; int dup = 0; for (peer = LIST_FIRST(&cfgstate.peerlist); peer; peer = LIST_NEXT(peer, link)) if (strcmp($2, peer->name) == 0) { dup++; break; } if (dup) free($2); else { peer = (struct syncpeer *)calloc(1, sizeof *peer); if (!peer) { log_err("config: calloc(1, %lu) " "failed", sizeof *peer); free($2); YYERROR; } peer->name = $2; } LIST_INSERT_HEAD(&cfgstate.peerlist, peer, link); cfgstate.peercnt++; log_msg(2, "config: add peer %s", peer->name); } | LISTEN ON STRING af port { char pstr[20]; if (cfgstate.listen_on) free(cfgstate.listen_on); cfgstate.listen_on = $3; cfgstate.listen_family = $4; cfgstate.listen_port = $5; if (cfgstate.listen_port < 1 || cfgstate.listen_port > 65534) { cfgstate.listen_port = SASYNCD_DEFAULT_PORT; log_msg(0, "config: bad port, listen-port " "reset to %u", SASYNCD_DEFAULT_PORT); } if ($5 != SASYNCD_DEFAULT_PORT) snprintf(pstr, sizeof pstr, "port %d",$5); log_msg(2, "config: listen on %s %s%s", cfgstate.listen_on, $4 == AF_INET6 ? "(IPv6) " : ($4 == AF_INET ? "(IPv4) " : ""), $5 != SASYNCD_DEFAULT_PORT ? pstr : ""); } | MODE modes | SHAREDKEY STRING { if (cfgstate.sharedkey) free(cfgstate.sharedkey); cfgstate.sharedkey = $2; log_msg(2, "config: shared key set"); } ; %% /* Program */ struct keyword { char *name; int value; }; static int match_cmp(const void *a, const void *b) { return strcmp(a, ((const struct keyword *)b)->name); } static int match(char *token) { /* Sorted */ static const struct keyword keywords[] = { { "flushmode", FLUSHMODE }, { "group", GROUP }, { "inet", INET }, { "inet6", INET6 }, { "interface", INTERFACE }, { "listen", LISTEN }, { "master", Y_MASTER }, { "mode", MODE }, { "never", NEVER }, { "on", ON }, { "peer", PEER }, { "port", PORT }, { "sharedkey", SHAREDKEY }, { "skipslave", SKIPSLAVE }, { "slave", Y_SLAVE }, { "startup", STARTUP }, { "sync", SYNC }, }; const struct keyword *k; k = bsearch(token, keywords, sizeof keywords / sizeof keywords[0], sizeof keywords[0], match_cmp); return k ? k->value : STRING; } int yylex(void) { char *p; int v; /* Locate next token */ if (!confptr) confptr = confbuf; else { for (p = confptr; *p && p < confbuf + conflen; p++) ; p++; if (!*p) return 0; confptr = p; } /* Numerical token? */ if (isdigit(*confptr)) { for (p = confptr; *p; p++) if (*p == '.') /* IP address, or bad input */ goto is_string; v = (int)strtol(confptr, (char **)NULL, 10); yylval.val = v; return VALUE; } is_string: v = match(confptr); if (v == STRING) { yylval.string = strdup(confptr); if (!yylval.string) { log_err("yylex: strdup()"); exit(1); } } return v; } static int conf_parse_file(char *cfgfile) { struct stat st; int fd, r; char *buf, *s, *d; struct passwd *pw = getpwnam(SASYNCD_USER); if (stat(cfgfile, &st) != 0) goto bad; /* Valid file? */ if ((st.st_uid && st.st_uid != pw->pw_uid) || ((st.st_mode & S_IFMT) != S_IFREG) || ((st.st_mode & (S_IRWXG | S_IRWXO)) != 0)) { log_msg(0, "configuration file has bad owner, type or mode"); goto bad; } fd = open(cfgfile, O_RDONLY, 0); if (fd < 0) goto bad; conflen = st.st_size; buf = (char *)malloc(conflen + 1); if (!buf) { log_err("malloc(%d) failed", conflen + 1); close(fd); return 1; } if (read(fd, buf, conflen) != conflen) { log_err("read() failed"); free(buf); close(fd); return 1; } close(fd); /* Prepare the buffer somewhat in the way of strsep() */ buf[conflen] = (char)0; for (s = buf, d = s; *s && s < buf + conflen; s++) { if (isspace(*s) && isspace(*(s+1))) continue; if (*s == '#') { while (*s != '\n' && s < buf + conflen) s++; continue; } if (d == buf && isspace(*s)) continue; *d++ = *s; } *d = (char)0; for (s = buf; s <= d; s++) if (isspace(*s)) *s = (char)0; confbuf = buf; confptr = NULL; r = yyparse(); free(buf); if (!cfgstate.carp_ifgroup) cfgstate.carp_ifgroup = strdup("carp"); return r; bad: log_msg(0, "failed to open \"%s\"", cfgfile); return 1; } int conf_init(int argc, char **argv) { char *cfgfile = 0; int ch; memset(&cfgstate, 0, sizeof cfgstate); cfgstate.runstate = INIT; LIST_INIT(&cfgstate.peerlist); cfgstate.listen_port = SASYNCD_DEFAULT_PORT; while ((ch = getopt(argc, argv, "c:dv")) != -1) { switch (ch) { case 'c': if (cfgfile) return 2; cfgfile = optarg; break; case 'd': cfgstate.debug++; break; case 'v': cfgstate.verboselevel++; break; default: return 2; } } argc -= optind; argv += optind; if (argc > 0) return 2; if (!cfgfile) cfgfile = SASYNCD_CFGFILE; if (conf_parse_file(cfgfile) == 0) { if (!cfgstate.sharedkey) { fprintf(stderr, "config: " "no shared key specified, cannot continue"); return 1; } return 0; } return 1; } void yyerror(const char *s) { fprintf(stderr, "config: %s\n", s); }