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 }