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
main(argc,argv)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
usage(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
parse_files_in_dir(const char * dirname)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
parse_file(char * filename)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
split_line(char * line,char * argvec[],int maxargvec)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
parse_params(char * famstr,char * typestr,char * protostr,char * path,const char * file,int line)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
parse_int(char * str)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
parse_filter_params(int argc,char ** argv)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