1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved. 23 */ 24 25 #include <stdio.h> 26 #include <sys/stat.h> 27 #include <stdlib.h> 28 #include <unistd.h> 29 #include <string.h> 30 #include <ctype.h> 31 #include <locale.h> 32 #include <sys/socket.h> 33 #include <sys/socketvar.h> 34 #include <errno.h> 35 36 #define MAXLINELEN 4096 37 38 /* 39 * Usage: 40 * sonconfig -f <file> 41 * Reads input from file. The file is structured as 42 * <fam> <type> <protocol> <path|module> 43 * <fam> <type> <protocol> 44 * with the first line registering and the second line 45 * deregistering. 46 * 47 * soconfig <fam> <type> <protocol> <path|module> 48 * registers 49 * 50 * soconfig <fam> <type> <protocol> 51 * deregisters 52 * 53 * Filter Operations (Consolidation Private): 54 * 55 * soconfig -F <name> <modname> {auto [top | bottom | before:filter | 56 * after:filter] | prog} <fam>:<type>:<proto>,... 57 * configure filter 58 * 59 * soconfig -F <name> 60 * unconfigures filter 61 */ 62 63 static int parse_file(char *filename); 64 65 static int split_line(char *line, char *argvec[], int maxargvec); 66 67 static int parse_params(char *famstr, char *typestr, char *protostr, 68 char *path, int line); 69 70 static int parse_int(char *str); 71 72 static void usage(void); 73 74 static int parse_filter_params(int argc, char **argv); 75 76 int 77 main(argc, argv) 78 int argc; 79 char *argv[]; 80 { 81 int ret; 82 83 argc--; argv++; 84 85 (void) setlocale(LC_ALL, ""); 86 #if !defined(TEXT_DOMAIN) 87 #define TEXT_DOMAIN "SYS_TEST" 88 #endif 89 (void) textdomain(TEXT_DOMAIN); 90 91 if (argc >= 2 && strcmp(argv[0], "-F") == 0) { 92 argc--; argv++; 93 ret = parse_filter_params(argc, argv); 94 exit(ret); 95 } 96 if (argc == 2 && strcmp(argv[0], "-f") == 0) { 97 ret = parse_file(argv[1]); 98 exit(ret); 99 } 100 if (argc == 3) { 101 ret = parse_params(argv[0], argv[1], argv[2], NULL, -1); 102 exit(ret); 103 } 104 if (argc == 4) { 105 ret = parse_params(argv[0], argv[1], argv[2], argv[3], -1); 106 exit(ret); 107 } 108 usage(); 109 exit(1); 110 /* NOTREACHED */ 111 } 112 113 static void 114 usage(void) 115 { 116 fprintf(stderr, gettext( 117 "Usage: soconfig -f <file>\n" 118 "\tsoconfig <fam> <type> <protocol> <path|module>\n" 119 "\tsoconfig <fam> <type> <protocol>\n")); 120 } 121 122 /* 123 * Open the specified file and parse each line. Skip comments (everything 124 * after a '#'). Return 1 if at least one error was encountered; otherwise 0. 125 */ 126 static int 127 parse_file(char *filename) 128 { 129 char line[MAXLINELEN]; 130 char pline[MAXLINELEN]; 131 int argcount; 132 char *argvec[20]; 133 FILE *fp; 134 int linecount = 0; 135 int numerror = 0; 136 137 fp = fopen(filename, "r"); 138 if (fp == NULL) { 139 perror("soconfig: open"); 140 fprintf(stderr, "\n"); 141 usage(); 142 return (1); 143 } 144 145 while (fgets(line, sizeof (line) - 1, fp) != NULL) { 146 linecount++; 147 strcpy(pline, line); 148 argcount = split_line(pline, argvec, 149 sizeof (argvec) / sizeof (argvec[0])); 150 #ifdef DEBUG 151 { 152 int i; 153 154 printf("scanned %d args\n", argcount); 155 for (i = 0; i < argcount; i++) 156 printf("arg[%d]: %s\n", i, argvec[i]); 157 } 158 #endif /* DEBUG */ 159 switch (argcount) { 160 case 0: 161 /* Empty line - or comment only line */ 162 break; 163 case 3: 164 numerror += parse_params(argvec[0], argvec[1], 165 argvec[2], NULL, linecount); 166 break; 167 case 4: 168 numerror += parse_params(argvec[0], argvec[1], 169 argvec[2], argvec[3], linecount); 170 break; 171 default: 172 numerror++; 173 fprintf(stderr, 174 gettext("Malformed line: <%s>\n"), line); 175 fprintf(stderr, 176 gettext("\ton line %d\n"), linecount); 177 break; 178 } 179 } 180 (void) fclose(fp); 181 182 if (numerror > 0) 183 return (1); 184 else 185 return (0); 186 } 187 188 /* 189 * Parse a line splitting it off at whitspace characters. 190 * Modifies the content of the string by inserting NULLs. 191 */ 192 static int 193 split_line(char *line, char *argvec[], int maxargvec) 194 { 195 int i = 0; 196 char *cp; 197 198 /* Truncate at the beginning of a comment */ 199 cp = strchr(line, '#'); 200 if (cp != NULL) 201 *cp = NULL; 202 203 /* CONSTCOND */ 204 while (1) { 205 /* Skip any whitespace */ 206 while (isspace(*line) && *line != NULL) 207 line++; 208 209 if (i >= maxargvec) 210 return (i); 211 212 argvec[i] = line; 213 if (*line == NULL) 214 return (i); 215 i++; 216 /* Skip until next whitespace */ 217 while (!isspace(*line) && *line != NULL) 218 line++; 219 if (*line != NULL) { 220 /* Break off argument */ 221 *line++ = NULL; 222 } 223 } 224 /* NOTREACHED */ 225 } 226 227 /* 228 * Parse the set of parameters and issues the sockconfig syscall. 229 * If line is not -1 it is assumed to be the line number in the file. 230 */ 231 static int 232 parse_params(char *famstr, char *typestr, char *protostr, char *path, int line) 233 { 234 int cmd, fam, type, protocol; 235 236 fam = parse_int(famstr); 237 if (fam == -1) { 238 fprintf(stderr, gettext("Bad family number: %s\n"), famstr); 239 if (line != -1) 240 fprintf(stderr, 241 gettext("\ton line %d\n"), line); 242 else { 243 fprintf(stderr, "\n"); 244 usage(); 245 } 246 return (1); 247 } 248 249 type = parse_int(typestr); 250 if (type == -1) { 251 fprintf(stderr, 252 gettext("Bad socket type number: %s\n"), typestr); 253 if (line != -1) 254 fprintf(stderr, 255 gettext("\ton line %d\n"), line); 256 else { 257 fprintf(stderr, "\n"); 258 usage(); 259 } 260 return (1); 261 } 262 263 protocol = parse_int(protostr); 264 if (protocol == -1) { 265 fprintf(stderr, 266 gettext("Bad protocol number: %s\n"), protostr); 267 if (line != -1) 268 fprintf(stderr, 269 gettext("\ton line %d\n"), line); 270 else { 271 fprintf(stderr, "\n"); 272 usage(); 273 } 274 return (1); 275 } 276 277 278 if (path != NULL) { 279 struct stat stats; 280 281 if (strncmp(path, "/dev", strlen("/dev")) == 0 && 282 stat(path, &stats) == -1) { 283 perror(path); 284 if (line != -1) 285 fprintf(stderr, 286 gettext("\ton line %d\n"), line); 287 else { 288 fprintf(stderr, "\n"); 289 usage(); 290 } 291 return (1); 292 } 293 294 cmd = SOCKCONFIG_ADD_SOCK; 295 } else { 296 cmd = SOCKCONFIG_REMOVE_SOCK; 297 } 298 299 #ifdef DEBUG 300 printf("not calling sockconfig(%d, %d, %d, %d, %s)\n", 301 cmd, fam, type, protocol, path == NULL ? "(null)" : path); 302 #else 303 if (_sockconfig(cmd, fam, type, protocol, path) == -1) { 304 perror("sockconfig"); 305 return (1); 306 } 307 #endif 308 return (0); 309 } 310 311 static int 312 parse_int(char *str) 313 { 314 char *end; 315 int res; 316 317 res = strtol(str, &end, 0); 318 if (end == str) 319 return (-1); 320 return (res); 321 } 322 323 /* 324 * Add and remove socket filters. 325 */ 326 static int 327 parse_filter_params(int argc, char **argv) 328 { 329 struct sockconfig_filter_props filprop; 330 sof_socktuple_t *socktuples; 331 size_t tupcnt, nalloc; 332 char *hintarg, *socktup, *tupstr; 333 int i; 334 335 if (argc == 1) { 336 if (_sockconfig(SOCKCONFIG_REMOVE_FILTER, argv[0], 0, 337 0, 0) < 0) { 338 switch (errno) { 339 case ENXIO: 340 fprintf(stderr, 341 gettext("socket filter is not configured " 342 "'%s'\n"), argv[0]); 343 break; 344 default: 345 perror("sockconfig"); 346 break; 347 } 348 return (1); 349 } 350 return (0); 351 } 352 353 if (argc < 4 || argc > 5) 354 return (1); 355 356 357 if (strlen(argv[1]) >= MODMAXNAMELEN) { 358 fprintf(stderr, 359 gettext("invalid module name '%s': name too long\n"), 360 argv[1]); 361 return (1); 362 } 363 filprop.sfp_modname = argv[1]; 364 365 /* Check the attach semantics */ 366 if (strcmp(argv[2], "auto") == 0) { 367 filprop.sfp_autoattach = B_TRUE; 368 if (argc == 5) { 369 /* placement hint */ 370 if (strcmp(argv[3], "top") == 0) { 371 filprop.sfp_hint = SOF_HINT_TOP; 372 } else if (strcmp(argv[3], "bottom") == 0) { 373 filprop.sfp_hint = SOF_HINT_BOTTOM; 374 } else { 375 if (strncmp(argv[3], "before", 6) == 0) { 376 filprop.sfp_hint = SOF_HINT_BEFORE; 377 } else if (strncmp(argv[3], "after", 5) == 0) { 378 filprop.sfp_hint = SOF_HINT_AFTER; 379 } else { 380 fprintf(stderr, 381 gettext("invalid placement hint " 382 "'%s'\n"), argv[3]); 383 return (1); 384 } 385 386 hintarg = strchr(argv[3], ':'); 387 if (hintarg == NULL || 388 (strlen(++hintarg) == 0) || 389 (strlen(hintarg) >= FILNAME_MAX)) { 390 fprintf(stderr, 391 gettext("invalid placement hint " 392 "argument '%s': name too long\n"), 393 argv[3]); 394 return (1); 395 } 396 397 filprop.sfp_hintarg = hintarg; 398 } 399 } else { 400 filprop.sfp_hint = SOF_HINT_NONE; 401 } 402 } else if (strcmp(argv[2], "prog") == 0) { 403 filprop.sfp_autoattach = B_FALSE; 404 filprop.sfp_hint = SOF_HINT_NONE; 405 /* cannot specify placement hint for programmatic filter */ 406 if (argc == 5) { 407 fprintf(stderr, 408 gettext("placement hint specified for programmatic " 409 "filter\n")); 410 return (1); 411 } 412 } else { 413 fprintf(stderr, gettext("invalid attach semantic '%s'\n"), 414 argv[2]); 415 return (1); 416 } 417 418 /* parse the socket tuples */ 419 nalloc = 4; 420 socktuples = calloc(nalloc, sizeof (sof_socktuple_t)); 421 if (socktuples == NULL) { 422 perror("calloc"); 423 return (1); 424 } 425 426 tupcnt = 0; 427 tupstr = argv[(argc == 4) ? 3 : 4]; 428 while ((socktup = strsep(&tupstr, ",")) != NULL) { 429 int val; 430 char *valstr; 431 432 if (tupcnt == nalloc) { 433 sof_socktuple_t *new; 434 435 nalloc *= 2; 436 new = realloc(socktuples, 437 nalloc * sizeof (sof_socktuple_t)); 438 if (new == NULL) { 439 perror("realloc"); 440 free(socktuples); 441 return (1); 442 } 443 socktuples = new; 444 } 445 i = 0; 446 while ((valstr = strsep(&socktup, ":")) != NULL && i < 3) { 447 val = parse_int(valstr); 448 if (val == -1) { 449 fprintf(stderr, gettext("bad socket tuple\n")); 450 free(socktuples); 451 return (1); 452 } 453 switch (i) { 454 case 0: socktuples[tupcnt].sofst_family = val; break; 455 case 1: socktuples[tupcnt].sofst_type = val; break; 456 case 2: socktuples[tupcnt].sofst_protocol = val; break; 457 } 458 i++; 459 } 460 if (i != 3) { 461 fprintf(stderr, gettext("bad socket tuple\n")); 462 free(socktuples); 463 return (1); 464 } 465 tupcnt++; 466 } 467 if (tupcnt == 0) { 468 fprintf(stderr, gettext("no socket tuples specified\n")); 469 free(socktuples); 470 return (1); 471 } 472 filprop.sfp_socktuple_cnt = tupcnt; 473 filprop.sfp_socktuple = socktuples; 474 475 if (_sockconfig(SOCKCONFIG_ADD_FILTER, argv[0], &filprop, 0, 0) < 0) { 476 switch (errno) { 477 case EINVAL: 478 fprintf(stderr, 479 gettext("invalid socket filter configuration\n")); 480 break; 481 case EEXIST: 482 fprintf(stderr, 483 gettext("socket filter is already configured " 484 "'%s'\n"), argv[0]); 485 break; 486 case ENOSPC: 487 fprintf(stderr, gettext("unable to satisfy placement " 488 "constraint\n")); 489 break; 490 default: 491 perror("sockconfig"); 492 break; 493 } 494 free(socktuples); 495 return (1); 496 } 497 free(socktuples); 498 return (0); 499 } 500