/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #define MAXLINELEN 4096 /* * Usage: * sonconfig -f * Reads input from file. The file is structured as * * * with the first line registering and the second line * deregistering. * * soconfig * registers * * soconfig * deregisters * * Filter Operations (Consolidation Private): * * soconfig -F {auto [top | bottom | before:filter | * after:filter] | prog} ::,... * configure filter * * soconfig -F * unconfigures filter */ static int parse_file(char *filename); static int split_line(char *line, char *argvec[], int maxargvec); static int parse_params(char *famstr, char *typestr, char *protostr, char *path, int line); static int parse_int(char *str); static void usage(void); static int parse_filter_params(int argc, char **argv); int main(argc, argv) int argc; char *argv[]; { int ret; argc--; argv++; (void) setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) #define TEXT_DOMAIN "SYS_TEST" #endif (void) textdomain(TEXT_DOMAIN); if (argc >= 2 && strcmp(argv[0], "-F") == 0) { argc--; argv++; ret = parse_filter_params(argc, argv); exit(ret); } if (argc == 2 && strcmp(argv[0], "-f") == 0) { ret = parse_file(argv[1]); exit(ret); } if (argc == 3) { ret = parse_params(argv[0], argv[1], argv[2], NULL, -1); exit(ret); } if (argc == 4) { ret = parse_params(argv[0], argv[1], argv[2], argv[3], -1); exit(ret); } usage(); exit(1); /* NOTREACHED */ } static void usage(void) { fprintf(stderr, gettext( "Usage: soconfig -f \n" "\tsoconfig \n" "\tsoconfig \n")); } /* * Open the specified file and parse each line. Skip comments (everything * after a '#'). Return 1 if at least one error was encountered; otherwise 0. */ static int parse_file(char *filename) { char line[MAXLINELEN]; char pline[MAXLINELEN]; int argcount; char *argvec[20]; FILE *fp; int linecount = 0; int numerror = 0; fp = fopen(filename, "r"); if (fp == NULL) { perror("soconfig: open"); fprintf(stderr, "\n"); usage(); return (1); } while (fgets(line, sizeof (line) - 1, fp) != NULL) { linecount++; strcpy(pline, line); argcount = split_line(pline, argvec, sizeof (argvec) / sizeof (argvec[0])); #ifdef DEBUG { int i; printf("scanned %d args\n", argcount); for (i = 0; i < argcount; i++) printf("arg[%d]: %s\n", i, argvec[i]); } #endif /* DEBUG */ switch (argcount) { case 0: /* Empty line - or comment only line */ break; case 3: numerror += parse_params(argvec[0], argvec[1], argvec[2], NULL, linecount); break; case 4: numerror += parse_params(argvec[0], argvec[1], argvec[2], argvec[3], linecount); break; default: numerror++; fprintf(stderr, gettext("Malformed line: <%s>\n"), line); fprintf(stderr, gettext("\ton line %d\n"), linecount); break; } } (void) fclose(fp); if (numerror > 0) return (1); else return (0); } /* * Parse a line splitting it off at whitspace characters. * Modifies the content of the string by inserting NULLs. */ static int split_line(char *line, char *argvec[], int maxargvec) { int i = 0; char *cp; /* Truncate at the beginning of a comment */ cp = strchr(line, '#'); if (cp != NULL) *cp = NULL; /* CONSTCOND */ while (1) { /* Skip any whitespace */ while (isspace(*line) && *line != NULL) line++; if (i >= maxargvec) return (i); argvec[i] = line; if (*line == NULL) return (i); i++; /* Skip until next whitespace */ while (!isspace(*line) && *line != NULL) line++; if (*line != NULL) { /* Break off argument */ *line++ = NULL; } } /* NOTREACHED */ } /* * Parse the set of parameters and issues the sockconfig syscall. * If line is not -1 it is assumed to be the line number in the file. */ static int parse_params(char *famstr, char *typestr, char *protostr, char *path, int line) { int cmd, fam, type, protocol; fam = parse_int(famstr); if (fam == -1) { fprintf(stderr, gettext("Bad family number: %s\n"), famstr); if (line != -1) fprintf(stderr, gettext("\ton line %d\n"), line); else { fprintf(stderr, "\n"); usage(); } return (1); } type = parse_int(typestr); if (type == -1) { fprintf(stderr, gettext("Bad socket type number: %s\n"), typestr); if (line != -1) fprintf(stderr, gettext("\ton line %d\n"), line); else { fprintf(stderr, "\n"); usage(); } return (1); } protocol = parse_int(protostr); if (protocol == -1) { fprintf(stderr, gettext("Bad protocol number: %s\n"), protostr); if (line != -1) fprintf(stderr, gettext("\ton line %d\n"), line); else { fprintf(stderr, "\n"); usage(); } return (1); } if (path != NULL) { struct stat stats; if (strncmp(path, "/dev", strlen("/dev")) == 0 && stat(path, &stats) == -1) { perror(path); if (line != -1) fprintf(stderr, gettext("\ton line %d\n"), line); else { fprintf(stderr, "\n"); usage(); } return (1); } cmd = SOCKCONFIG_ADD_SOCK; } else { cmd = SOCKCONFIG_REMOVE_SOCK; } #ifdef DEBUG printf("not calling sockconfig(%d, %d, %d, %d, %s)\n", cmd, fam, type, protocol, path == NULL ? "(null)" : path); #else if (_sockconfig(cmd, fam, type, protocol, path) == -1) { perror("sockconfig"); return (1); } #endif return (0); } static int parse_int(char *str) { char *end; int res; res = strtol(str, &end, 0); if (end == str) return (-1); return (res); } /* * Add and remove socket filters. */ static int parse_filter_params(int argc, char **argv) { struct sockconfig_filter_props filprop; sof_socktuple_t *socktuples; size_t tupcnt, nalloc; char *hintarg, *socktup, *tupstr; int i; if (argc == 1) { if (_sockconfig(SOCKCONFIG_REMOVE_FILTER, argv[0], 0, 0, 0) < 0) { switch (errno) { case ENXIO: fprintf(stderr, gettext("socket filter is not configured " "'%s'\n"), argv[0]); break; default: perror("sockconfig"); break; } return (1); } return (0); } if (argc < 4 || argc > 5) return (1); if (strlen(argv[1]) >= MODMAXNAMELEN) { fprintf(stderr, gettext("invalid module name '%s': name too long\n"), argv[1]); return (1); } filprop.sfp_modname = argv[1]; /* Check the attach semantics */ if (strcmp(argv[2], "auto") == 0) { filprop.sfp_autoattach = B_TRUE; if (argc == 5) { /* placement hint */ if (strcmp(argv[3], "top") == 0) { filprop.sfp_hint = SOF_HINT_TOP; } else if (strcmp(argv[3], "bottom") == 0) { filprop.sfp_hint = SOF_HINT_BOTTOM; } else { if (strncmp(argv[3], "before", 6) == 0) { filprop.sfp_hint = SOF_HINT_BEFORE; } else if (strncmp(argv[3], "after", 5) == 0) { filprop.sfp_hint = SOF_HINT_AFTER; } else { fprintf(stderr, gettext("invalid placement hint " "'%s'\n"), argv[3]); return (1); } hintarg = strchr(argv[3], ':'); if (hintarg == NULL || (strlen(++hintarg) == 0) || (strlen(hintarg) >= FILNAME_MAX)) { fprintf(stderr, gettext("invalid placement hint " "argument '%s': name too long\n"), argv[3]); return (1); } filprop.sfp_hintarg = hintarg; } } else { filprop.sfp_hint = SOF_HINT_NONE; } } else if (strcmp(argv[2], "prog") == 0) { filprop.sfp_autoattach = B_FALSE; filprop.sfp_hint = SOF_HINT_NONE; /* cannot specify placement hint for programmatic filter */ if (argc == 5) { fprintf(stderr, gettext("placement hint specified for programmatic " "filter\n")); return (1); } } else { fprintf(stderr, gettext("invalid attach semantic '%s'\n"), argv[2]); return (1); } /* parse the socket tuples */ nalloc = 4; socktuples = calloc(nalloc, sizeof (sof_socktuple_t)); if (socktuples == NULL) { perror("calloc"); return (1); } tupcnt = 0; tupstr = argv[(argc == 4) ? 3 : 4]; while ((socktup = strsep(&tupstr, ",")) != NULL) { int val; char *valstr; if (tupcnt == nalloc) { sof_socktuple_t *new; nalloc *= 2; new = realloc(socktuples, nalloc * sizeof (sof_socktuple_t)); if (new == NULL) { perror("realloc"); free(socktuples); return (1); } socktuples = new; } i = 0; while ((valstr = strsep(&socktup, ":")) != NULL && i < 3) { val = parse_int(valstr); if (val == -1) { fprintf(stderr, gettext("bad socket tuple\n")); free(socktuples); return (1); } switch (i) { case 0: socktuples[tupcnt].sofst_family = val; break; case 1: socktuples[tupcnt].sofst_type = val; break; case 2: socktuples[tupcnt].sofst_protocol = val; break; } i++; } if (i != 3) { fprintf(stderr, gettext("bad socket tuple\n")); free(socktuples); return (1); } tupcnt++; } if (tupcnt == 0) { fprintf(stderr, gettext("no socket tuples specified\n")); free(socktuples); return (1); } filprop.sfp_socktuple_cnt = tupcnt; filprop.sfp_socktuple = socktuples; if (_sockconfig(SOCKCONFIG_ADD_FILTER, argv[0], &filprop, 0, 0) < 0) { switch (errno) { case EINVAL: fprintf(stderr, gettext("invalid socket filter configuration\n")); break; case EEXIST: fprintf(stderr, gettext("socket filter is already configured " "'%s'\n"), argv[0]); break; case ENOSPC: fprintf(stderr, gettext("unable to satisfy placement " "constraint\n")); break; default: perror("sockconfig"); break; } free(socktuples); return (1); } free(socktuples); return (0); }