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 <ctype.h> 26 #include <dirent.h> 27 #include <errno.h> 28 #include <locale.h> 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <sys/socket.h> 33 #include <sys/socketvar.h> 34 #include <sys/stat.h> 35 #include <unistd.h> 36 37 #define MAXLINELEN 4096 38 39 /* 40 * Usage: 41 * soconfig -d <dir> 42 * Reads input from files in dir. 43 * 44 * soconfig -f <file> 45 * Reads input from file. The file is structured as 46 * <fam> <type> <protocol> <path|module> 47 * <fam> <type> <protocol> 48 * with the first line registering and the second line 49 * deregistering. 50 * 51 * soconfig <fam> <type> <protocol> <path|module> 52 * registers 53 * 54 * soconfig <fam> <type> <protocol> 55 * deregisters 56 * 57 * Filter Operations (Consolidation Private): 58 * 59 * soconfig -F <name> <modname> {auto [top | bottom | before:filter | 60 * after:filter] | prog} <fam>:<type>:<proto>,... 61 * configure filter 62 * 63 * soconfig -F <name> 64 * unconfigures filter 65 */ 66 67 static int parse_files_in_dir(const char *dir); 68 69 static int parse_file(char *filename); 70 71 static int split_line(char *line, char *argvec[], int maxargvec); 72 73 static int parse_params(char *famstr, char *typestr, char *protostr, 74 char *path, const char *file, int line); 75 76 static int parse_int(char *str); 77 78 static void usage(void); 79 80 static int parse_filter_params(int argc, char **argv); 81 82 int 83 main(argc, argv) 84 int argc; 85 char *argv[]; 86 { 87 int ret; 88 89 argc--; argv++; 90 91 (void) setlocale(LC_ALL, ""); 92 #if !defined(TEXT_DOMAIN) 93 #define TEXT_DOMAIN "SYS_TEST" 94 #endif 95 (void) textdomain(TEXT_DOMAIN); 96 97 if (argc >= 2 && strcmp(argv[0], "-F") == 0) { 98 argc--; argv++; 99 ret = parse_filter_params(argc, argv); 100 exit(ret); 101 } 102 if (argc == 2 && strcmp(argv[0], "-d") == 0) { 103 ret = parse_files_in_dir(argv[1]); 104 exit(ret); 105 } 106 if (argc == 2 && strcmp(argv[0], "-f") == 0) { 107 ret = parse_file(argv[1]); 108 exit(ret); 109 } 110 if (argc == 3) { 111 ret = parse_params(argv[0], argv[1], argv[2], NULL, NULL, -1); 112 exit(ret); 113 } 114 if (argc == 4) { 115 ret = parse_params(argv[0], argv[1], argv[2], argv[3], 116 NULL, -1); 117 exit(ret); 118 } 119 usage(); 120 exit(1); 121 /* NOTREACHED */ 122 } 123 124 static void 125 usage(void) 126 { 127 fprintf(stderr, gettext( 128 "Usage: soconfig -d <dir>\n" 129 "\tsoconfig -f <file>\n" 130 "\tsoconfig <fam> <type> <protocol> <path|module>\n" 131 "\tsoconfig <fam> <type> <protocol>\n")); 132 } 133 134 /* 135 * Parse all files in the given directory. 136 */ 137 static int 138 parse_files_in_dir(const char *dirname) 139 { 140 DIR *dp; 141 struct dirent *dirp; 142 struct stat stats; 143 char buf[MAXPATHLEN]; 144 145 if ((dp = opendir(dirname)) == NULL) { 146 fprintf(stderr, gettext("failed to open directory '%s': %s\n"), 147 dirname, strerror(errno)); 148 return (1); 149 } 150 151 while ((dirp = readdir(dp)) != NULL) { 152 if (dirp->d_name[0] == '.') 153 continue; 154 155 if (snprintf(buf, sizeof (buf), "%s/%s", dirname, 156 dirp->d_name) >= sizeof (buf)) { 157 fprintf(stderr, 158 gettext("path name is too long: %s/%s\n"), 159 dirname, dirp->d_name); 160 continue; 161 } 162 if (stat(buf, &stats) == -1) { 163 fprintf(stderr, 164 gettext("failed to stat '%s': %s\n"), buf, 165 strerror(errno)); 166 continue; 167 } 168 if (!S_ISREG(stats.st_mode)) 169 continue; 170 171 (void) parse_file(buf); 172 } 173 174 closedir(dp); 175 176 return (0); 177 } 178 179 /* 180 * Open the specified file and parse each line. Skip comments (everything 181 * after a '#'). Return 1 if at least one error was encountered; otherwise 0. 182 */ 183 static int 184 parse_file(char *filename) 185 { 186 char line[MAXLINELEN]; 187 char pline[MAXLINELEN]; 188 int argcount; 189 char *argvec[20]; 190 FILE *fp; 191 int linecount = 0; 192 int numerror = 0; 193 194 fp = fopen(filename, "r"); 195 if (fp == NULL) { 196 perror("soconfig: open"); 197 fprintf(stderr, "\n"); 198 usage(); 199 return (1); 200 } 201 202 while (fgets(line, sizeof (line) - 1, fp) != NULL) { 203 linecount++; 204 strcpy(pline, line); 205 argcount = split_line(pline, argvec, 206 sizeof (argvec) / sizeof (argvec[0])); 207 #ifdef DEBUG 208 { 209 int i; 210 211 printf("scanned %d args\n", argcount); 212 for (i = 0; i < argcount; i++) 213 printf("arg[%d]: %s\n", i, argvec[i]); 214 } 215 #endif /* DEBUG */ 216 switch (argcount) { 217 case 0: 218 /* Empty line - or comment only line */ 219 break; 220 case 3: 221 numerror += parse_params(argvec[0], argvec[1], 222 argvec[2], NULL, filename, linecount); 223 break; 224 case 4: 225 numerror += parse_params(argvec[0], argvec[1], 226 argvec[2], argvec[3], filename, linecount); 227 break; 228 default: 229 numerror++; 230 fprintf(stderr, 231 gettext("Malformed line: <%s>\n"), line); 232 fprintf(stderr, 233 gettext("\ton line %d in %s\n"), linecount, 234 filename); 235 break; 236 } 237 } 238 (void) fclose(fp); 239 240 if (numerror > 0) 241 return (1); 242 else 243 return (0); 244 } 245 246 /* 247 * Parse a line splitting it off at whitspace characters. 248 * Modifies the content of the string by inserting NULLs. 249 */ 250 static int 251 split_line(char *line, char *argvec[], int maxargvec) 252 { 253 int i = 0; 254 char *cp; 255 256 /* Truncate at the beginning of a comment */ 257 cp = strchr(line, '#'); 258 if (cp != NULL) 259 *cp = NULL; 260 261 /* CONSTCOND */ 262 while (1) { 263 /* Skip any whitespace */ 264 while (isspace(*line) && *line != NULL) 265 line++; 266 267 if (i >= maxargvec) 268 return (i); 269 270 argvec[i] = line; 271 if (*line == NULL) 272 return (i); 273 i++; 274 /* Skip until next whitespace */ 275 while (!isspace(*line) && *line != NULL) 276 line++; 277 if (*line != NULL) { 278 /* Break off argument */ 279 *line++ = NULL; 280 } 281 } 282 /* NOTREACHED */ 283 } 284 285 /* 286 * Parse the set of parameters and issues the sockconfig syscall. 287 * If line is not -1 it is assumed to be the line number in the file. 288 */ 289 static int 290 parse_params(char *famstr, char *typestr, char *protostr, char *path, 291 const char *file, int line) 292 { 293 int cmd, fam, type, protocol; 294 295 fam = parse_int(famstr); 296 if (fam == -1) { 297 fprintf(stderr, gettext("Bad family number: %s\n"), famstr); 298 if (line != -1) 299 fprintf(stderr, 300 gettext("\ton line %d in %s\n"), line, file); 301 else { 302 fprintf(stderr, "\n"); 303 usage(); 304 } 305 return (1); 306 } 307 308 type = parse_int(typestr); 309 if (type == -1) { 310 fprintf(stderr, 311 gettext("Bad socket type number: %s\n"), typestr); 312 if (line != -1) 313 fprintf(stderr, 314 gettext("\ton line %d in %s\n"), line, file); 315 else { 316 fprintf(stderr, "\n"); 317 usage(); 318 } 319 return (1); 320 } 321 322 protocol = parse_int(protostr); 323 if (protocol == -1) { 324 fprintf(stderr, 325 gettext("Bad protocol number: %s\n"), protostr); 326 if (line != -1) 327 fprintf(stderr, 328 gettext("\ton line %d in %s\n"), line, file); 329 else { 330 fprintf(stderr, "\n"); 331 usage(); 332 } 333 return (1); 334 } 335 336 337 if (path != NULL) { 338 struct stat stats; 339 340 if (strncmp(path, "/dev", strlen("/dev")) == 0 && 341 stat(path, &stats) == -1) { 342 perror(path); 343 if (line != -1) 344 fprintf(stderr, 345 gettext("\ton line %d in %s\n"), line, 346 file); 347 else { 348 fprintf(stderr, "\n"); 349 usage(); 350 } 351 return (1); 352 } 353 354 cmd = SOCKCONFIG_ADD_SOCK; 355 } else { 356 cmd = SOCKCONFIG_REMOVE_SOCK; 357 } 358 359 #ifdef DEBUG 360 printf("not calling sockconfig(%d, %d, %d, %d, %s)\n", 361 cmd, fam, type, protocol, path == NULL ? "(null)" : path); 362 #else 363 if (_sockconfig(cmd, fam, type, protocol, path) == -1) { 364 char *s; 365 366 switch (errno) { 367 case EEXIST: 368 s = gettext("Mapping exists"); 369 break; 370 default: 371 s = strerror(errno); 372 break; 373 } 374 375 fprintf(stderr, 376 gettext("warning: socket configuration failed " 377 "for family %d type %d protocol %d: %s\n"), 378 fam, type, protocol, s); 379 if (line != -1) { 380 fprintf(stderr, 381 gettext("\ton line %d in %s\n"), line, file); 382 } 383 return (1); 384 } 385 #endif 386 return (0); 387 } 388 389 static int 390 parse_int(char *str) 391 { 392 char *end; 393 int res; 394 395 res = strtol(str, &end, 0); 396 if (end == str) 397 return (-1); 398 return (res); 399 } 400 401 /* 402 * Add and remove socket filters. 403 */ 404 static int 405 parse_filter_params(int argc, char **argv) 406 { 407 struct sockconfig_filter_props filprop; 408 sof_socktuple_t *socktuples; 409 size_t tupcnt, nalloc; 410 char *hintarg, *socktup, *tupstr; 411 int i; 412 413 if (argc == 1) { 414 if (_sockconfig(SOCKCONFIG_REMOVE_FILTER, argv[0], 0, 415 0, 0) < 0) { 416 switch (errno) { 417 case ENXIO: 418 fprintf(stderr, 419 gettext("socket filter is not configured " 420 "'%s'\n"), argv[0]); 421 break; 422 default: 423 perror("sockconfig"); 424 break; 425 } 426 return (1); 427 } 428 return (0); 429 } 430 431 if (argc < 4 || argc > 5) 432 return (1); 433 434 435 if (strlen(argv[1]) >= MODMAXNAMELEN) { 436 fprintf(stderr, 437 gettext("invalid module name '%s': name too long\n"), 438 argv[1]); 439 return (1); 440 } 441 filprop.sfp_modname = argv[1]; 442 443 /* Check the attach semantics */ 444 if (strcmp(argv[2], "auto") == 0) { 445 filprop.sfp_autoattach = B_TRUE; 446 if (argc == 5) { 447 /* placement hint */ 448 if (strcmp(argv[3], "top") == 0) { 449 filprop.sfp_hint = SOF_HINT_TOP; 450 } else if (strcmp(argv[3], "bottom") == 0) { 451 filprop.sfp_hint = SOF_HINT_BOTTOM; 452 } else { 453 if (strncmp(argv[3], "before", 6) == 0) { 454 filprop.sfp_hint = SOF_HINT_BEFORE; 455 } else if (strncmp(argv[3], "after", 5) == 0) { 456 filprop.sfp_hint = SOF_HINT_AFTER; 457 } else { 458 fprintf(stderr, 459 gettext("invalid placement hint " 460 "'%s'\n"), argv[3]); 461 return (1); 462 } 463 464 hintarg = strchr(argv[3], ':'); 465 if (hintarg == NULL || 466 (strlen(++hintarg) == 0) || 467 (strlen(hintarg) >= FILNAME_MAX)) { 468 fprintf(stderr, 469 gettext("invalid placement hint " 470 "argument '%s': name too long\n"), 471 argv[3]); 472 return (1); 473 } 474 475 filprop.sfp_hintarg = hintarg; 476 } 477 } else { 478 filprop.sfp_hint = SOF_HINT_NONE; 479 } 480 } else if (strcmp(argv[2], "prog") == 0) { 481 filprop.sfp_autoattach = B_FALSE; 482 filprop.sfp_hint = SOF_HINT_NONE; 483 /* cannot specify placement hint for programmatic filter */ 484 if (argc == 5) { 485 fprintf(stderr, 486 gettext("placement hint specified for programmatic " 487 "filter\n")); 488 return (1); 489 } 490 } else { 491 fprintf(stderr, gettext("invalid attach semantic '%s'\n"), 492 argv[2]); 493 return (1); 494 } 495 496 /* parse the socket tuples */ 497 nalloc = 4; 498 socktuples = calloc(nalloc, sizeof (sof_socktuple_t)); 499 if (socktuples == NULL) { 500 perror("calloc"); 501 return (1); 502 } 503 504 tupcnt = 0; 505 tupstr = argv[(argc == 4) ? 3 : 4]; 506 while ((socktup = strsep(&tupstr, ",")) != NULL) { 507 int val; 508 char *valstr; 509 510 if (tupcnt == nalloc) { 511 sof_socktuple_t *new; 512 513 nalloc *= 2; 514 new = realloc(socktuples, 515 nalloc * sizeof (sof_socktuple_t)); 516 if (new == NULL) { 517 perror("realloc"); 518 free(socktuples); 519 return (1); 520 } 521 socktuples = new; 522 } 523 i = 0; 524 while ((valstr = strsep(&socktup, ":")) != NULL && i < 3) { 525 val = parse_int(valstr); 526 if (val == -1) { 527 fprintf(stderr, gettext("bad socket tuple\n")); 528 free(socktuples); 529 return (1); 530 } 531 switch (i) { 532 case 0: socktuples[tupcnt].sofst_family = val; break; 533 case 1: socktuples[tupcnt].sofst_type = val; break; 534 case 2: socktuples[tupcnt].sofst_protocol = val; break; 535 } 536 i++; 537 } 538 if (i != 3) { 539 fprintf(stderr, gettext("bad socket tuple\n")); 540 free(socktuples); 541 return (1); 542 } 543 tupcnt++; 544 } 545 if (tupcnt == 0) { 546 fprintf(stderr, gettext("no socket tuples specified\n")); 547 free(socktuples); 548 return (1); 549 } 550 filprop.sfp_socktuple_cnt = tupcnt; 551 filprop.sfp_socktuple = socktuples; 552 553 if (_sockconfig(SOCKCONFIG_ADD_FILTER, argv[0], &filprop, 0, 0) < 0) { 554 switch (errno) { 555 case EINVAL: 556 fprintf(stderr, 557 gettext("invalid socket filter configuration\n")); 558 break; 559 case EEXIST: 560 fprintf(stderr, 561 gettext("socket filter is already configured " 562 "'%s'\n"), argv[0]); 563 break; 564 case ENOSPC: 565 fprintf(stderr, gettext("unable to satisfy placement " 566 "constraint\n")); 567 break; 568 default: 569 perror("sockconfig"); 570 break; 571 } 572 free(socktuples); 573 return (1); 574 } 575 free(socktuples); 576 return (0); 577 } 578