xref: /illumos-gate/usr/src/cmd/nlsadmin/nlsadmin.c (revision 9b9d39d2a32ff806d2431dbcc50968ef1e6d46b2)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 /*
31  * nlsadmin.c -- control program for the network listener service
32  *
33  * This program replaces a previous version of nlsadmin.
34  *
35  * This version of nlsadmin works with the service access facility to
36  * control the network listener.  The functionality of the SVR3.2 nlsadmin
37  * command is supported through calls to the more general sacadm and pmadm
38  * commands available through SAF.  Users should migrate away from nlsadmin
39  * to sacadm and pmadm for these functions.
40  *
41  * The -m option of the SVR3.2 nlsadmin command is now ignored.
42  *
43  * The -t option associates an address with service code 1 (same as in SVR3.2).
44  * The -l option associates an address with service code 0.
45  *
46  * nlsadmin also contains new functionality -- the ability to format a
47  * "listener-specific" string to put in the _pmtab database.  This
48  * functionality is required by SAF.
49  */
50 
51 #include <sys/types.h>
52 #include <sys/stat.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <ctype.h>
56 #include <errno.h>
57 #include <string.h>
58 #include <sac.h>
59 #include "nlsadmin.h"
60 
61 #define OPTIONS	"a:c:d:e:ikl:mo:p:qr:st:vw:xy:z:A:N:VDR:"
62 #ifndef FALSE
63 #define TRUE	1
64 #define FALSE	0
65 #endif
66 /*
67  * defines for -q exit codes: QZERO is used for conditions that the
68  * man page documents as returning 0, QONE for those that return 1
69  */
70 #define QZERO	0
71 #define QONE	1
72 
73 /*
74  * defines for simulated standard error format code
75  */
76 #define MM_NOSEV        0
77 #define MM_HALT         1
78 #define MM_ERROR        2
79 #define MM_WARNING      3
80 #define MM_INFO         4
81 
82 char	*Nlsname;		/* set to argv[0]			*/
83 char	Label[25];		/* label component for fmtmsg		*/
84 int	Quietflag = FALSE;	/* set to TRUE when -q used		*/
85 
86 extern	int errno;
87 void	nlsmesg();
88 uid_t	geteuid();
89 char	*nexttok();
90 char	*pflags();
91 char	*gencmdstr();
92 
93 struct	svcfields {
94 	char	*pmtag;
95 	char	*pmtype;
96 	char	*svc_code;
97 	char	*flags;
98 	char	*id;
99 	char	*res1;
100 	char	*res2;
101 	char	*res3;
102 	char	*addr;
103 	char	*rpc;
104 	char	*lflags;
105 	char	*modules;
106 	char	*command;
107 	char	*comment;
108 };
109 
110 void no_permission(void) __NORETURN;
111 void usage(int flag);
112 
113 int
114 main(int argc, char **argv)
115 {
116 	extern	char *optarg;
117 	extern	int optind;
118 	int	c;			/* used for return from getopt  */
119 	char	*addrptr = NULL;	/* set when -A address is used	*/
120 	char	*rpcptr = NULL;		/* set when -R rpcinfo is used	*/
121 	char	*cmdptr = NULL;		/* set with -c command		*/
122 	char	*comptr = NULL;		/* set with -y comment (old)	*/
123 	char	*idptr = NULL;		/* set with -w id (old)		*/
124 	char	*lptr = NULL;		/* set with -l addr (old)	*/
125 	char	*moduleptr = NULL;	/* set with -m modules		*/
126 	char	*pipeptr = NULL;	/* set with -o pipe		*/
127 	char	*svcptr = NULL;		/* set when service code used (old) */
128 	char	*tptr = NULL;		/* set when -t addr used (old)	*/
129 	char	*netspec = NULL;	/* set to the network specification */
130 	int	flag = 0;		/* bit flag of type of command	*/
131 	int	exitcode = 0;		/* exit status of this command	*/
132 	int	lflags = 0;		/* listener flags		*/
133 	char	buf[BUFSIZ];		/* temp buffer #1		*/
134 	char	mesg[BUFSIZ];		/* temp buffer #2		*/
135 	FILE	*fp;			/* used for checking netspec	*/
136 	char	*ptr;			/* temp pointer			*/
137 	char	*ptr2;			/* temp pointer			*/
138 	int	sawsep = 0;		/* flag for RPC separator	*/
139 
140 	Nlsname = argv[0];
141 	sprintf(Label, "UX:%.14s", argv[0]);	/* for standard message fmt */
142 
143 	while ((c = getopt(argc, argv, OPTIONS)) != -1) {
144 		switch (c) {
145 		case 'a':
146 			if ( (flag && (flag != CMDFLAG)) || svcptr || Quietflag
147 			      || addrptr || rpcptr || lflags)
148 				usage(INCONSISTENT);
149 			svcptr = optarg;
150 			break;
151 		case 'c':
152 			if ( (flag && (flag != CMDFLAG)) || cmdptr || Quietflag )
153 				usage(INCONSISTENT);
154 			cmdptr = optarg;
155 			flag |= CMDFLAG;
156 			break;
157 		case 'd':
158 			if ( flag || svcptr || Quietflag || comptr || addrptr
159 			     || rpcptr || cmdptr || idptr || lflags )
160 				usage(INCONSISTENT);
161 			svcptr = optarg;
162 			flag |= DISFLAG;
163 			break;
164 		case 'e':
165 			if ( flag || svcptr || Quietflag || comptr || addrptr
166 			     || rpcptr || cmdptr || idptr || lflags )
167 				usage(INCONSISTENT);
168 			svcptr = optarg;
169 			flag |= ENAFLAG;
170 			break;
171 		case 'i':
172 			if ( flag || svcptr || Quietflag || comptr || addrptr
173 			     || rpcptr || cmdptr || idptr || lflags )
174 				usage(INCONSISTENT);
175 			flag |= INIFLAG;
176 			break;
177 		case 'k':
178 			if ( flag || svcptr || Quietflag || comptr || addrptr
179 			     || rpcptr || cmdptr || idptr || lflags )
180 				usage(INCONSISTENT);
181 			flag |= KILFLAG;
182 			break;
183 		case 'l':
184 			if ( ( flag && (flag != ADRFLAG)) || svcptr || lptr
185 			      || Quietflag || comptr || addrptr || rpcptr
186 			      || cmdptr || idptr || lflags )
187 				usage(INCONSISTENT);
188 			lptr = optarg;
189 			flag |= ADRFLAG;
190 			break;
191 		case 'm':
192 			if ( (flag && (flag != CMDFLAG)) || Quietflag || rpcptr || lflags )
193 				usage(INCONSISTENT);
194 			flag |= CMDFLAG;
195 			break;
196 		case 'o':
197 			if ( flag || svcptr || Quietflag || comptr || idptr || netspec )
198 				usage(INCONSISTENT);
199 			pipeptr = optarg;
200 			flag |= PIPFLAG;
201 			break;
202 		case 'p':
203 			if ( (flag && (flag != CMDFLAG) && (flag != PIPFLAG)) || Quietflag )
204 				usage(INCONSISTENT);
205 			moduleptr = optarg;
206 			break;
207 		case 'q':
208 			if ( (flag && (flag != ZZZFLAG)) || Quietflag || comptr
209 			     || rpcptr || lflags || idptr )
210 				usage(INCONSISTENT);
211 			Quietflag = TRUE;
212 			break;
213 		case 'r':
214 			if ( flag || svcptr || Quietflag || comptr || addrptr
215 			     || rpcptr || cmdptr || idptr || lflags )
216 				usage(INCONSISTENT);
217 			flag |= REMFLAG;
218 			svcptr = optarg;
219 			break;
220 		case 's':
221 			if ( flag || svcptr || Quietflag || comptr || addrptr
222 			     || rpcptr || cmdptr || idptr || lflags )
223 				usage(INCONSISTENT);
224 			flag |= STAFLAG;
225 			break;
226 		case 't':
227 			if ( (flag && (flag != ADRFLAG)) || svcptr || tptr
228 			      || Quietflag || comptr || addrptr || rpcptr
229 			      || cmdptr || idptr || lflags )
230 				usage(INCONSISTENT);
231 			tptr = optarg;
232 			flag |= ADRFLAG;
233 			break;
234 		case 'v':
235 			if ( flag || svcptr || Quietflag || comptr || rpcptr
236 			     || addrptr || idptr || lflags )
237 				usage(INCONSISTENT);
238 			flag |= VBSFLAG;
239 			break;
240 		case 'w':
241 			if ( (flag && (flag != CMDFLAG)) || Quietflag || idptr
242 			     || rpcptr || addrptr || lflags )
243 				usage(INCONSISTENT);
244 			idptr = optarg;
245 			break;
246 		case 'x':
247 			if ( flag || svcptr || Quietflag || netspec || comptr
248 			     || rpcptr || addrptr || lflags || idptr )
249 				usage(INCONSISTENT);
250 			flag |= NETFLAG;
251 			break;
252 		case 'y':
253 			if ( (flag && (flag != CMDFLAG)) || Quietflag || comptr
254 			     || rpcptr || addrptr || lflags )
255 				usage(INCONSISTENT);
256 			comptr = optarg;
257 			break;
258 		case 'z':
259 			if ( flag || svcptr || comptr || addrptr || rpcptr
260 			     || idptr || lflags )
261 				usage(INCONSISTENT);
262 			flag |= ZZZFLAG;
263 			svcptr = optarg;
264 			break;
265 		case 'A':
266 			if ( (flag && (flag != CMDFLAG) && (flag != PIPFLAG))
267 			     || netspec || svcptr || idptr || comptr )
268 				usage(INCONSISTENT);
269 			addrptr = optarg;
270 			break;
271 		case 'D':
272 			if ( (flag && (flag != CMDFLAG) && (flag != PIPFLAG))
273 			     || netspec || svcptr || idptr || comptr || addrptr
274 			     || lflags )
275 				usage(INCONSISTENT);
276 			lflags |= DFLAG;
277 			break;
278 		case 'N':
279 			if ( netspec )
280 				usage(INCONSISTENT);
281 			netspec = optarg;
282 			break;
283 		case 'R':
284 			if ( (flag && (flag != CMDFLAG) && (flag != PIPFLAG))
285 			     || netspec || svcptr || idptr || comptr )
286 				usage(INCONSISTENT);
287 			for (ptr = optarg; *ptr; ++ptr) {
288 				if ((*ptr == ':') && !sawsep) {
289 					/*
290 					 * skip separator - note that if
291 					 * separator has been seen, it's not
292 					 * a digit so it will generate a usage
293 					 * message below like we want
294 					 */
295 					sawsep++;
296 					continue;
297 				}
298 				if (!isdigit(*ptr))
299 					usage(USAGE);
300 			}
301 			ptr = strchr(optarg, ':');
302 			if (ptr)
303 				/* change the ':' to a ',' */
304 				*ptr = ',';
305 			else
306 				usage(USAGE);
307 			rpcptr = optarg;
308 			break;
309 		case 'V':
310 			if ( flag || svcptr || Quietflag || comptr || netspec
311 			     || rpcptr || addrptr || idptr || lflags )
312 				usage(INCONSISTENT);
313 			flag |= VERFLAG;
314 			break;
315 		case '?':
316 			usage(USAGE);
317 		}
318 		/* NOTREACHED */
319 	}
320 
321 	if ((optind < argc) && ! netspec)
322 		netspec = argv[optind++];
323 	if (optind < argc)
324 		usage(USAGE);
325 
326 
327 	/* determine if this command requires a netspec */
328 	if (flag != CMDFLAG) {
329 		/* if flag is CMDFLAG, more complicated checking of netspec
330 		 * is done below in switch
331 		 */
332 		if ((flag == PIPFLAG || flag == VERFLAG || flag == NETFLAG)) {
333 			if (netspec)
334 				usage(USAGE);
335 		}
336 		else if (!netspec)
337 			usage(USAGE);
338 	}
339 
340 	if (netspec && (flag != INIFLAG)) {
341 		sprintf(buf, SAC_LSPM, netspec);
342 
343 		if ((fp = popen(buf, "r")) == NULL) {
344 			nlsmesg(MM_ERROR, "System error");
345 			exit(NLS_SYSERR);
346 		}
347 
348 		if (fgets(buf, BUFSIZ, fp) == NULL) {
349 			nlsmesg(MM_ERROR, "Invalid network specification");
350 			exit(NLS_BADPM);
351 		}
352 		else {
353 			ptr = strchr(buf, ':');
354 			ptr++;
355 			ptr2 = strchr(ptr, ':');
356 			*ptr2 = '\0';
357 			if (strcmp(ptr, LISTENTYPE) != 0) {
358 				sprintf(mesg, "Network specification \"%s\" is not of type %s", netspec, LISTENTYPE);
359 				nlsmesg(MM_ERROR, mesg);
360 				exit(NLS_BADPM);
361 			}
362 		}
363 
364 		pclose(fp);
365 	}
366 
367 	if (svcptr) {
368 		/* check to see if service code is "correct" -- right range
369 		 * and format.  The -m flag is ignored, so no check for
370 		 * "administrative" service codes (0-100) is done.
371 		 */
372 		c = strlen(svcptr);
373 		if ((c == 0) || (c >= SVC_CODE_SZ)) {
374 			sprintf(mesg, "Service code contains more than %d characters", SVC_CODE_SZ);
375 			nlsmesg(MM_ERROR, mesg);
376 			exit(NLS_SERV);
377 		}
378 	}
379 
380 	switch (flag) {
381 	default:
382 		usage(USAGE);
383 		break;
384 	case NONE:
385 		if ( svcptr || comptr || rpcptr || lflags || idptr )
386 			usage(INCONSISTENT);
387 		exitcode = prt_nets(netspec);
388 		break;
389 	case INIFLAG:
390 		if (geteuid() != ROOT)
391 			no_permission();
392 		exitcode = add_pm(netspec);
393 		break;
394 	case CMDFLAG:
395 		if ( svcptr || comptr || idptr || netspec ) {
396 			if (geteuid() != ROOT)
397 				no_permission();
398 			if ((exitcode = old_addsvc(svcptr, "", cmdptr, comptr, moduleptr, idptr, NULL, netspec)) != NLS_OK)
399 				switch (exitcode) {
400 				case NLS_SERV:
401 					nlsmesg(MM_ERROR, "Service code already exists");
402 					break;
403 				default:
404 					nlsmesg(MM_ERROR, "Could not add service");
405 					break;
406 				}
407 		}
408 		else {
409 			if (netspec)
410 				usage(INCONSISTENT);
411 			exitcode = prt_cmd(cmdptr, CFLAG | lflags, moduleptr, addrptr, rpcptr);
412 		}
413 		break;
414 	case PIPFLAG:
415 		if (geteuid() != ROOT)
416 			no_permission();
417 		exitcode = prt_cmd(pipeptr, PFLAG | lflags, moduleptr, addrptr, rpcptr);
418 		break;
419 	case VERFLAG:
420 		printf("%d\n", VERSION);
421 		exit(NLS_OK);
422 		break;
423 	case DISFLAG:
424 		if (geteuid() != ROOT)
425 			no_permission();
426 		exitcode = disable_svc(svcptr, netspec);
427 		break;
428 	case ENAFLAG:
429 		if (geteuid() != ROOT)
430 			no_permission();
431 		exitcode = enable_svc(svcptr, netspec);
432 		break;
433 	case KILFLAG:
434 		if (geteuid() != ROOT)
435 			no_permission();
436 		exitcode = kill_listener(netspec);
437 		break;
438 	case ADRFLAG:
439 		/* check for root permissions in setup_addr */
440 		exitcode = setup_addr(lptr, tptr, netspec);
441 		break;
442 	case REMFLAG:
443 		if (geteuid() != ROOT)
444 			no_permission();
445 		exitcode = remove_svc(svcptr, netspec, TRUE);
446 		break;
447 	case STAFLAG:
448 		if (geteuid() != ROOT)
449 			no_permission();
450 		exitcode = start_listener(netspec);
451 		break;
452 	case VBSFLAG:
453 		exitcode = prt_svcs(NULL, netspec);
454 		break;
455 	case NETFLAG:
456 		exitcode = prt_nets(NULL);
457 		break;
458 	case ZZZFLAG:
459 		exitcode = prt_svcs(svcptr, netspec);
460 		break;
461 	}
462 	if (exitcode == NLS_SYSERR)
463 		nlsmesg(MM_ERROR, "System error in SAC command");
464 	return (exitcode);
465 }
466 
467 
468 static char umsg[] = "usage: %s -x\n\
469        %s [ options ] netspec\n\
470        %s [ options ] -N port_monitor_tag\n\
471        %s -V\n\
472        %s -c cmd | -o pipename [ -p modules ] [ -A addr | -D ] \\\n\
473           [ -R prognum:versnum ]\n\
474 \n\
475        [ options ] are:\n\
476        [ -a svc_code -c \"cmd\" -y \"cmt\" [-p modules] [-w id] ]\n\
477        [-q] | [-v] | [-s] | [-k] | [-i] |\n\
478        [-e svc_code] | [-d svc_code] | [-r svc_code] | [[-q] -z svc_code]\n\
479        [[-l addr | -] [-t addr | -]] |\n\
480 ";
481 
482 void
483 usage(int flag)
484 {
485 	switch (flag) {
486 	case INCONSISTENT:
487 		nlsmesg(MM_ERROR, "Inconsistent options");
488 		break;
489 	case MISSINGARG:
490 		nlsmesg(MM_ERROR, "Missing argument");
491 		break;
492 	case USAGE:
493 		break;
494 	}
495 	fprintf(stderr, umsg, Nlsname, Nlsname, Nlsname, Nlsname, Nlsname);
496 	exit(NLS_CMD);
497 }
498 
499 
500 /*
501  * no_permission:  print out error message and exit when the user needs to
502  *                 needs to be root and isn't.
503  */
504 
505 void
506 no_permission(void)
507 {
508 	nlsmesg(MM_ERROR, "Must be super user");
509 	exit(NLS_PERM);
510 }
511 
512 /*
513  * nlsmesg:  print out either an error or a warning message.  severity must
514  *           be either MM_ERROR or MM_WARNING.  this routine will be converted
515  *           to use the standard message format later.
516  */
517 
518 void
519 nlsmesg(int severity, char *text)
520 {
521 	int	class;
522 
523 	if (severity == MM_ERROR)
524 		fprintf(stderr, "%s: error: %s\n", Nlsname, text);
525 	else
526 		fprintf(stderr, "%s: warning: %s\n", Nlsname, text);
527 	return;
528 }
529 
530 /*
531  * prt_cmd:  print out the listener-dependent string for sacadm.
532  */
533 
534 int
535 prt_cmd(char *path, long flags, char *modules, char *addr, char *rpcp)
536 	/* path: full path of command or pipe	*/
537 	/* flags: listener flags		*/
538 	/* PFLAG for pipe			*/
539 	/* CFLAG for command			*/
540 	/* DFLAG for dynamic addr		*/
541  	/* modules: STREAMS modules to push	*/
542 	/* addr: private address		*/
543 	/* rpcp: RPC prog and ver #		*/
544 {
545 	struct	stat	sbuf;
546 	char	mesgbuf[BUFSIZ];
547 	char	*tmp;
548 
549 	if (*path != '/') {
550 		nlsmesg(MM_ERROR, "Must specify full path name");
551 		return(NLS_CMD);
552 	}
553 
554 	if ((tmp = strchr(path, ' ')) != NULL)
555 		*tmp = '\0';
556 
557 	if (stat(path, &sbuf) < 0) {
558 		if (errno != EFAULT) {
559 			sprintf(mesgbuf, "%s does not exist", path);
560 			nlsmesg(MM_WARNING, mesgbuf);
561 		}
562 		else
563 			return(NLS_SYSERR);
564 	}
565 
566 	if (tmp)
567 		*tmp = ' ';
568 
569 	printf("%s:%s:%s:%s:%s\n", (addr ? addr : ""), (rpcp ? rpcp : ""),
570 		pflags(flags), (modules ? modules : ""), path);
571 	return(NLS_OK);
572 }
573 
574 /*
575  * old_addsvc:  use pmadm to add a service code to the listener.  this will
576  *              not allow specification of a private address -- use pmadm!
577  */
578 
579 int
580 old_addsvc(char *svc, char *addr, char *cmd, char *com, char *module,
581     char *id, char *flags, char *netspec)
582 {
583 	char	buf[BUFSIZ];
584 	char	mesgbuf[BUFSIZ];
585 	int	rtn;
586 	struct	stat	sbuf;
587 	char	*tmp;
588 
589 	if (!svc || !cmd || !com || !netspec)
590 		usage(MISSINGARG);
591 
592 	/* create "port-monitor specific" info in the same way as prt_cmd */
593 
594 	if (*cmd != '/') {
595 		nlsmesg(MM_ERROR, "Must specify full path name");
596 		return(NLS_CMD);
597 	}
598 
599 	if ((tmp = strchr(cmd, ' ')) != NULL)
600 		*tmp = '\0';
601 
602 	if (stat(cmd, &sbuf) < 0) {
603 		if (errno != EFAULT) {
604 			sprintf(mesgbuf, "%s does not exist", cmd);
605 			nlsmesg(MM_WARNING, mesgbuf);
606 		}
607 		else
608 			return(NLS_SYSERR);
609 	}
610 
611 	if (tmp)
612 		*tmp = ' ';
613 
614 	if (addr)
615 		sprintf(mesgbuf, "'%s::c:%s:%s'", addr, module ? module : "" , cmd);
616 	else
617 		sprintf(mesgbuf, "'::c:%s:%s'", module ? module : "" , cmd);
618 
619 	if (flags && *flags)
620 		sprintf(buf, PM_ADDSVCF, netspec, svc, (id)?id:DEFAULTID, flags, mesgbuf, VERSION, com ? com : "");
621 	else
622 		sprintf(buf, PM_ADDSVC, netspec, svc, (id)?id:DEFAULTID, mesgbuf, VERSION, com ? com : "");
623 
624 	if ((rtn = system(buf)) < 0) {
625 		return(NLS_SYSERR);
626 	}
627 	rtn = (rtn>>8) & 0xff;	/* get child return value out of exit word */
628 
629 	switch (rtn) {
630 	case 0:
631 		return(NLS_OK);
632 		break;
633 	case E_BADARGS:
634 	case E_SAFERR:
635 	case E_SYSERR:
636 	case E_NOEXIST:
637 	case E_PMRUN:
638 	case E_PMNOTRUN:
639 	case E_RECOVER:
640 	case E_SACNOTRUN:
641 	default:
642 		return(NLS_SYSERR);
643 		break;
644 	case E_DUP:
645 		return(NLS_SERV);
646 		break;
647 	case E_NOPRIV:
648 		no_permission();
649 		break;
650 	}
651 	/* NOTREACHED */
652 }
653 
654 /*
655  * prt_nets:  print the status of one network, or all nets if netspec
656  *            is NULL
657  */
658 int
659 prt_nets(char *netspec)
660 {
661 	char	buf[BUFSIZ];
662 	FILE	*fp;
663 	char	*name;
664 	char	*state;
665 	char	*type;
666 	int	found = FALSE;
667 	int	rtn = NLS_OK;
668 
669 	if (netspec == NULL)
670 		sprintf(buf, SAC_LSTY, LISTENTYPE);
671 	else
672 		sprintf(buf, SAC_LSPM, netspec);
673 
674 	if ((fp = popen(buf, "r")) == NULL)
675 		return(NLS_SYSERR);
676 
677 	while (fgets(buf, BUFSIZ, fp) != NULL) {
678 		if ((name = nexttok(buf, ":")) == NULL)
679 			return(NLS_SYSERR);
680 		if ((type = nexttok(NULL, ":")) == NULL)
681 			return(NLS_SYSERR);
682 
683 		if (strcmp(type, LISTENTYPE) != 0)
684 			continue;  /* ignore other types of port monitors */
685 
686 		found = TRUE;
687 		if (nexttok(NULL, ":") == NULL)
688 			return(NLS_SYSERR);
689 		if (nexttok(NULL, ":") == NULL)
690 			return(NLS_SYSERR);
691 		if ((state = nexttok(NULL, ":")) == NULL)
692 			return(NLS_SYSERR);
693 		if (strcmp(state, "ENABLED") == 0 ||
694 		    strcmp(state, "STARTING") == 0) {
695 			rtn = QZERO;
696 			if (!Quietflag)
697 				printf("%s\t%s\n", name, "ACTIVE");
698 		}
699 		else {
700 			rtn = QONE;
701 			if (!Quietflag)
702 				printf("%s\t%s\n", name, "INACTIVE");
703 		}
704 	}
705 	pclose(fp);
706 
707 	if (netspec && !found) {
708 		nlsmesg(MM_ERROR, "Invalid network specification");
709 		return(NLS_BADPM);
710 	}
711 
712 	if (netspec)
713 		return(rtn);
714 	else
715 		return(NLS_OK);
716 
717 }
718 
719 
720 /*
721  * print info about service on netspec, or all services on netspec
722  * if svc is NULL
723  */
724 
725 int
726 prt_svcs(char *svc, char *netspec)
727 {
728 	char	buf[BUFSIZ];
729 	char	mesg[BUFSIZ];
730 	FILE	*fp;
731 	struct	svcfields entry;
732 	int	rtn;
733 	int	found = FALSE;
734 	char	*p;
735 
736 	if (svc == NULL)
737 		sprintf(buf, PM_LSALL, netspec);
738 	else
739 		sprintf(buf, PM_LSONE, netspec, svc);
740 
741 	if ((fp = popen(buf, "r")) == NULL)
742 		return(NLS_SYSERR);
743 
744 	while (fgets(buf, BUFSIZ, fp) != NULL) {
745 		if ((rtn = svc_format(buf, &entry)) != 0) {
746 			switch (rtn) {
747 			case NOTLISTEN:
748 				continue;
749 				break;
750 			case BADPMFMT:
751 				return(NLS_SYSERR);
752 				break;
753 			case BADLISFMT:
754 				sprintf(mesg, "Entry for code \"%s\" has incorrect format", entry.svc_code);
755 				nlsmesg(MM_WARNING, mesg);
756 				continue;
757 				break;
758 			}
759 		}
760 		found = TRUE;
761 
762 		if (!Quietflag) {
763 			printf("%s\t", entry.svc_code);
764 			if (*entry.addr)
765 				printf("%s\t", entry.addr);
766 			else if (strchr(entry.lflags, 'd'))
767 				printf("DYNAMIC\t");
768 			else
769 				printf("NOADDR\t");
770 
771 			if (strchr(entry.flags, 'x') == NULL)
772 				printf("ENABLED \t");
773 			else
774 				printf("DISABLED\t");
775 
776 
777 			printf("%s\t%s\t%s\t%s\t# %s",
778 				(*entry.rpc)?entry.rpc:"NORPC", entry.id,
779 				(*entry.modules)?entry.modules:"NOMODULES",
780 				entry.command, (*entry.comment)?entry.comment:"");
781 		}
782 		else {
783 			if (strchr(entry.flags, 'x') == NULL)
784 				return(QZERO);
785 			else
786 				return(QONE);
787 		}
788 	}
789 
790 	pclose(fp);
791 
792 	if (rtn == NOTLISTEN) { /* check last return to see if error */
793 		sprintf(mesg, "Network specification \"%s\" is not of type %s", netspec, LISTENTYPE);
794 		nlsmesg(MM_ERROR, mesg);
795 		return(NLS_BADPM);
796 	}
797 	if (svc && !found) {
798 		if (!Quietflag) {
799 			sprintf(mesg, "Service \"%s\" unknown", svc);
800 			nlsmesg(MM_ERROR, mesg);
801 		}
802 		return(NLS_SERV);
803 	}
804 
805 	return(NLS_OK);
806 }
807 
808 /*
809  * disable_svc:  use pmadm to disable a service
810  */
811 
812 int
813 disable_svc(char *svc, char *netspec)
814 {
815 	char	buf[BUFSIZ];
816 	int	rtn;
817 
818 	sprintf(buf, PM_DISABLE, netspec, svc);
819 
820 	if ((rtn = system(buf)) < 0) {
821 		return(NLS_SYSERR);
822 	}
823 	rtn = (rtn>>8) & 0xff;	/* get child return value out of exit word */
824 
825 	switch (rtn) {
826 	case 0:
827 		return(NLS_OK);
828 		break;
829 	case E_BADARGS:
830 	case E_SAFERR:
831 	case E_SYSERR:
832 	case E_PMRUN:
833 	case E_PMNOTRUN:
834 	case E_RECOVER:
835 	case E_SACNOTRUN:
836 	default:
837 		return(NLS_SYSERR);
838 		break;
839 	case E_NOEXIST:
840 	case E_DUP:
841 		nlsmesg(MM_ERROR, "Non-existent service.");
842 		return(NLS_SERV);
843 		break;
844 	case E_NOPRIV:
845 		no_permission();
846 		break;
847 	}
848 	/* NOTREACHED */
849 }
850 
851 
852 int
853 enable_svc(char *svc, char *netspec)
854 {
855 	char	buf[BUFSIZ];
856 	int	rtn;
857 
858 	sprintf(buf, PM_ENABLE, netspec, svc);
859 
860 	if ((rtn = system(buf)) < 0) {
861 		return(NLS_SYSERR);
862 	}
863 	rtn = (rtn>>8) & 0xff;	/* get child return value out of exit word */
864 
865 	switch (rtn) {
866 	case 0:
867 		return(NLS_OK);
868 		break;
869 	case E_BADARGS:
870 	case E_SAFERR:
871 	case E_SYSERR:
872 	case E_PMRUN:
873 	case E_PMNOTRUN:
874 	case E_RECOVER:
875 	case E_SACNOTRUN:
876 	default:
877 		return(NLS_SYSERR);
878 		break;
879 	case E_NOEXIST:
880 	case E_DUP:
881 		nlsmesg(MM_ERROR, "Non-existent service.");
882 		return(NLS_SERV);
883 		break;
884 	case E_NOPRIV:
885 		no_permission();
886 		break;
887 	}
888 	/* NOTREACHED */
889 }
890 
891 
892 int
893 remove_svc(char *svc, char *netspec, int printerrors)
894 {
895 	char	buf[BUFSIZ];
896 	int	rtn;
897 
898 	sprintf(buf, PM_REMSVC, netspec, svc);
899 
900 	if ((rtn = system(buf)) < 0) {
901 		return(NLS_SYSERR);
902 	}
903 	rtn = (rtn>>8) & 0xff;	/* get child return value out of exit word */
904 
905 	switch (rtn) {
906 	case 0:
907 		return(NLS_OK);
908 		break;
909 	case E_BADARGS:
910 	case E_SAFERR:
911 	case E_SYSERR:
912 	case E_PMRUN:
913 	case E_PMNOTRUN:
914 	case E_RECOVER:
915 	case E_SACNOTRUN:
916 	default:
917 		return(NLS_SYSERR);
918 		break;
919 	case E_NOEXIST:
920 	case E_DUP:
921 		if (printerrors)
922 			nlsmesg(MM_ERROR, "Non-existent service.");
923 		return(NLS_SERV);
924 		break;
925 	case E_NOPRIV:
926 		no_permission();
927 		break;
928 	}
929 	/* NOTREACHED */
930 }
931 
932 
933 int
934 kill_listener(char *netspec)
935 {
936 	char	buf[BUFSIZ];
937 	char	mesg[BUFSIZ];
938 	int	rtn;
939 
940 	sprintf(buf, SAC_KILLPM, netspec);
941 
942 	if ((rtn = system(buf)) < 0) {
943 		return(NLS_SYSERR);
944 	}
945 	rtn = (rtn>>8) & 0xff;	/* get child return value out of exit word */
946 
947 	switch (rtn) {
948 	case 0:
949 		return(NLS_OK);
950 		break;
951 	case E_BADARGS:
952 	case E_DUP:
953 	case E_SAFERR:
954 	case E_SYSERR:
955 	case E_PMRUN:
956 	case E_RECOVER:
957 	case E_SACNOTRUN:
958 	default:
959 		return(NLS_SYSERR);
960 		break;
961 	case E_PMNOTRUN:
962 		sprintf(mesg, "No listener active on network \"%s\"", netspec);
963 		nlsmesg(MM_ERROR, mesg);
964 		return(NLS_FAILED);
965 	case E_NOEXIST:
966 		nlsmesg(MM_ERROR, "Non-existent port monitor.");
967 		return(NLS_SERV);
968 		break;
969 	case E_NOPRIV:
970 		no_permission();
971 		break;
972 	}
973 	/* NOTREACHED */
974 }
975 
976 
977 /*
978  * add_pm:  add a port monitor (initialize directories) using sacadm
979  */
980 
981 int
982 add_pm(char *netspec)
983 {
984 	char	buf[BUFSIZ];
985 	char	mesg[BUFSIZ];
986 	int	rtn;
987 
988 	sprintf(buf, SAC_ADDPM, netspec, LISTENTYPE, gencmdstr(netspec), VERSION);
989 
990 	if ((rtn = system(buf)) < 0) {
991 		return(NLS_SYSERR);
992 	}
993 	rtn = (rtn>>8) & 0xff;	/* get child return value out of exit word */
994 
995 	switch (rtn) {
996 	case 0:
997 		old_addsvc(NLPSSVCCODE, NULL, NLPSSRV, "NLPS server", "", "root", NULL, netspec);
998 		return(NLS_OK);
999 		break;
1000 	case E_BADARGS:
1001 	case E_SAFERR:
1002 	case E_SYSERR:
1003 	case E_RECOVER:
1004 	case E_NOEXIST:
1005 	case E_PMNOTRUN:
1006 	case E_SACNOTRUN:
1007 	default:
1008 		return(NLS_SYSERR);
1009 		break;
1010 	case E_DUP:
1011 	case E_PMRUN:
1012 		nlsmesg(MM_ERROR, "Listener already initialized");
1013 		return(NLS_FAILED);
1014 		break;
1015 	case E_NOPRIV:
1016 		no_permission();
1017 		break;
1018 	}
1019 	/* NOTREACHED */
1020 }
1021 
1022 
1023 /*
1024  * gencmdstr:  generate the correct string to invoke the listener (starlan
1025  *             requires special handling)
1026  */
1027 
1028 char *
1029 gencmdstr(char *netspec)
1030 {
1031 	static char buf[BUFSIZ];
1032 
1033 	(void) strcpy(buf, LISTENCMD);
1034 	if (!strcmp(netspec, "starlan"))
1035 		(void) strcat(buf, " -m slan");
1036 	(void) strcat(buf, " ");
1037 	(void) strcat(buf, netspec);
1038 	return(buf);
1039 }
1040 
1041 
1042 /*
1043  * start_listener: start the listener
1044  */
1045 
1046 int
1047 start_listener(char *netspec)
1048 {
1049 	char	buf[BUFSIZ];
1050 	char	scratch[BUFSIZ];
1051 	int	rtn;
1052 
1053 	sprintf(buf, SAC_STARTPM, netspec);
1054 
1055 	if ((rtn = system(buf)) < 0)
1056 		return(NLS_SYSERR);
1057 	rtn = (rtn>>8) & 0xff;
1058 	switch (rtn) {
1059 	case 0:
1060 		break;
1061 	case E_BADARGS:
1062 	case E_SAFERR:
1063 	case E_SYSERR:
1064 	case E_RECOVER:
1065 	case E_PMNOTRUN:
1066 	case E_SACNOTRUN:
1067 	default:
1068 		return(NLS_SYSERR);
1069 		break;
1070 	case E_NOEXIST:
1071 	case E_DUP:
1072 		nlsmesg(MM_ERROR, "Non-existent port monitor.");
1073 		return(NLS_BADPM);
1074 		break;
1075 	case E_PMRUN:
1076 		nlsmesg(MM_ERROR, "Listener already running");
1077 		return(NLS_FAILED);
1078 	case E_NOPRIV:
1079 		no_permission();
1080 		break;
1081 	}
1082 
1083 	sprintf(buf, SAC_ENABLPM, netspec);
1084 
1085 	if ((rtn = system(buf)) < 0) {
1086 		return(NLS_SYSERR);
1087 	}
1088 	rtn = (rtn>>8) & 0xff;
1089 	switch (rtn) {
1090 	case 0:
1091 		return(NLS_OK);
1092 		break;
1093 	case E_BADARGS:
1094 	case E_SAFERR:
1095 	case E_SYSERR:
1096 	case E_RECOVER:
1097 	case E_SACNOTRUN:
1098 	default:
1099 		return(NLS_SYSERR);
1100 		break;
1101 	case E_NOEXIST:
1102 	case E_DUP:
1103 		nlsmesg(MM_ERROR, "Non-existent port monitor.");
1104 		return(NLS_BADPM);
1105 		break;
1106 	case E_PMRUN:
1107 		nlsmesg(MM_ERROR, "Listener already running");
1108 		return(NLS_FAILED);
1109 	case E_PMNOTRUN:
1110 		nlsmesg(MM_ERROR, "Listener start failed");
1111 		return(NLS_FAILED);
1112 	case E_NOPRIV:
1113 		no_permission();
1114 		break;
1115 	}
1116 	/* NOTREACHED */
1117 }
1118 
1119 
1120 /*
1121  * setup_addr:  setup the -l and -t addresses.
1122  */
1123 
1124 int
1125 setup_addr(char *laddr, char *taddr, char *netspec)
1126 {
1127 	char	buf[BUFSIZ];
1128 	char	mesg[BUFSIZ];
1129 	char	*p;
1130 	int	rtn;
1131 	int	qlisten = FALSE;
1132 	int	qtty = FALSE;
1133 	FILE	*fp;
1134 	struct	svcfields entry;
1135 
1136 	if (laddr && *laddr == '-')
1137 		qlisten = TRUE;
1138 
1139 	if (taddr && *taddr == '-')
1140 		qtty = TRUE;
1141 
1142 	if (laddr) {
1143 		sprintf(buf, PM_LSONE, netspec, NLPSSVCCODE);
1144 
1145 		if ((fp = popen(buf, "r")) == NULL) {
1146 			return(NLS_SYSERR);
1147 		}
1148 
1149 		if (fgets(buf, BUFSIZ, fp) != NULL) {
1150 			if ((rtn = svc_format(buf, &entry)) != 0) {
1151 				switch (rtn) {
1152 				case NOTLISTEN:
1153 					nlsmesg(MM_ERROR, "Incorrect port monitor type.  Must be of type listen");
1154 					return(NLS_FAILED);
1155 					break;
1156 				case BADPMFMT:
1157 					return(NLS_SYSERR);
1158 					break;
1159 				case BADLISFMT:
1160 					sprintf(mesg, "Entry for code \"%s\" has incorrect format", entry.svc_code);
1161 					nlsmesg(MM_WARNING, mesg);
1162 					break;
1163 				}
1164 			}
1165 			else {
1166 				if (qlisten)
1167 					printf("%s\n", entry.addr);
1168 				else {
1169 					if (geteuid() != ROOT)
1170 						no_permission();
1171 					/* add address */
1172 					remove_svc(NLPSSVCCODE, netspec, FALSE);
1173 					p = strchr(entry.comment, '\n');
1174 					if (p)
1175 						*p = '\0';
1176 					old_addsvc(NLPSSVCCODE, laddr, entry.command, entry.comment, entry.modules, entry.id, entry.flags, netspec);
1177 				}
1178 			}
1179 			pclose(fp);
1180 		}
1181 		else if (!qlisten)
1182 			nlsmesg(MM_WARNING, "NLPS service not defined");
1183 	}
1184 	if (taddr) {
1185 		sprintf(buf, PM_LSONE, netspec, TTYSVCCODE);
1186 
1187 		if ((fp = popen(buf, "r")) == NULL) {
1188 			return(NLS_SYSERR);
1189 		}
1190 
1191 		if (fgets(buf, BUFSIZ, fp) != NULL) {
1192 			if ((rtn = svc_format(buf, &entry)) != 0) {
1193 				switch (rtn) {
1194 				case NOTLISTEN:
1195 					nlsmesg(MM_ERROR, "Incorrect port monitor type.  Must be of type listen");
1196 					return(NLS_FAILED);
1197 					break;
1198 				case BADPMFMT:
1199 					return(NLS_SYSERR);
1200 					break;
1201 				case BADLISFMT:
1202 					sprintf(mesg, "Entry for code \"%s\" has incorrect format", entry.svc_code);
1203 					nlsmesg(MM_WARNING, mesg);
1204 					break;
1205 				}
1206 			}
1207 			else {
1208 				if (qtty)
1209 					printf("%s\n", entry.addr);
1210 				else {
1211 					if (geteuid() != ROOT)
1212 						no_permission();
1213 					/* add address */
1214 					remove_svc(TTYSVCCODE, netspec, FALSE);
1215 					p = strchr(entry.comment, '\n');
1216 					if (p)
1217 						*p = '\0';
1218 					old_addsvc(TTYSVCCODE, taddr, entry.command, entry.comment, entry.modules, entry.id, entry.flags, netspec);
1219 				}
1220 			}
1221 			pclose(fp);
1222 		}
1223 		else if (!qtty)
1224 			nlsmesg(MM_WARNING, "remote login service not defined");
1225 	}
1226 	return(NLS_OK);
1227 }
1228 
1229 
1230 /*
1231  * svc_format:  scan a line of output from pmadm to separate it into fields.
1232  *              returns BADPMFMT for missing fields or incorrect syntax.
1233  *                      NOTLISTEN is the port monitor type is not listen.
1234  *                      BADLISFMT if the listener-specific data is incorrect.
1235  *                      NLS_OK if everything checked out and data is broken
1236  *                             into the structure.
1237  */
1238 
1239 int
1240 svc_format(char *buf, struct svcfields *entry)
1241 {
1242 	char	*ptr;		/* temporary pointer into buffer	*/
1243 	char	*tmp;		/* temporary pointer into buffer	*/
1244 
1245 	entry->pmtag = buf;
1246 	if ((ptr = strchr(buf, ':')) == NULL)
1247 		return(BADPMFMT);
1248 	*ptr++ = '\0';
1249 	entry->pmtype = ptr;
1250 	if ((ptr = strchr(entry->pmtype, ':')) == NULL)
1251 		return(BADPMFMT);
1252 	*ptr++ = '\0';
1253 	entry->svc_code = ptr;
1254 
1255 	if (strcmp(entry->pmtype, LISTENTYPE) != 0)
1256 		return(NOTLISTEN);
1257 
1258 	if ((ptr = strchr(entry->svc_code, ':')) == NULL)
1259 		return(BADPMFMT);
1260 	*ptr++ = '\0';
1261 	entry->flags = ptr;
1262 	if ((ptr = strchr(entry->flags, ':')) == NULL)
1263 		return(BADPMFMT);
1264 	*ptr++ = '\0';
1265 	entry->id = ptr;
1266 	if ((ptr = strchr(entry->id, ':')) == NULL)
1267 		return(BADPMFMT);
1268 	*ptr++ = '\0';
1269 	entry->res1 = ptr;
1270 	if ((ptr = strchr(entry->res1, ':')) == NULL)
1271 		return(BADPMFMT);
1272 	*ptr++ = '\0';
1273 	entry->res2 = ptr;
1274 	if ((ptr = strchr(entry->res2, ':')) == NULL)
1275 		return(BADPMFMT);
1276 	*ptr++ = '\0';
1277 	entry->res3 = ptr;
1278 	if ((ptr = strchr(entry->res3, ':')) == NULL)
1279 		return(BADPMFMT);
1280 	*ptr++ = '\0';
1281 	entry->addr = ptr;
1282 	if ((ptr = strchr(entry->addr, ':')) == NULL)
1283 		return(BADLISFMT);
1284 	*ptr++ = '\0';
1285 	entry->rpc = ptr;
1286 	if ((ptr = strchr(entry->rpc, ':')) == NULL)
1287 		return(BADLISFMT);
1288 	*ptr++ = '\0';
1289 	if (*entry->rpc) {
1290 		if ((tmp = strchr(entry->rpc, ',')) == NULL)
1291 			return(BADLISFMT);
1292 		*tmp = ':';
1293 	}
1294 	entry->lflags = ptr;
1295 	if ((ptr = strchr(entry->lflags, ':')) == NULL)
1296 		return(BADLISFMT);
1297 	*ptr++ = '\0';
1298 	entry->modules = ptr;
1299 	if ((ptr = strchr(entry->modules, ':')) == NULL)
1300 		return(BADLISFMT);
1301 	*ptr++ = '\0';
1302 	entry->command = ptr;
1303 	if ((ptr = strchr(entry->command, '#')) == NULL)
1304 		return(BADLISFMT);
1305 	*ptr++ = '\0';
1306 	entry->comment = ptr;
1307 	return(NLS_OK);
1308 }
1309 
1310 
1311 /*
1312  * nexttok - return next token, essentially a strtok, but it can
1313  *	deal with null fields and strtok can not
1314  *
1315  *	args:	str - the string to be examined, NULL if we should
1316  *		      examine the remembered string
1317  *		delim - the list of valid delimiters
1318  */
1319 
1320 
1321 char *
1322 nexttok(char *str, char *delim)
1323 {
1324 	static char *savep;	/* the remembered string */
1325 	register char *p;	/* pointer to start of token */
1326 	register char *ep;	/* pointer to end of token */
1327 
1328 	p = (str == NULL) ? savep : str ;
1329 	if (p == NULL)
1330 		return(NULL);
1331 	ep = strpbrk(p, delim);
1332 	if (ep == NULL) {
1333 		savep = NULL;
1334 		return(p);
1335 	}
1336 	savep = ep + 1;
1337 	*ep = '\0';
1338 	return(p);
1339 }
1340 
1341 
1342 /*
1343  * pflags - put flags into intelligible form for output
1344  *
1345  *	args:	flags - binary representation of flags
1346  */
1347 
1348 char *
1349 pflags(long flags)
1350 {
1351 	register int i;			/* scratch counter */
1352 	static char buf[BUFSIZ];	/* formatted flags */
1353 
1354 	if (flags == 0)
1355 		return("");
1356 	i = 0;
1357 	if (flags & CFLAG) {
1358 		buf[i++] = 'c';
1359 		flags &= ~CFLAG;
1360 	}
1361 	if (flags & DFLAG) {
1362 		buf[i++] = 'd';
1363 		flags &= ~DFLAG;
1364 	}
1365 	if (flags & PFLAG) {
1366 		buf[i++] = 'p';
1367 		flags &= ~PFLAG;
1368 	}
1369 	if (flags) {
1370 		nlsmesg(MM_ERROR, "Internal error in pflags");
1371 		exit(NLS_FAILED);
1372 	}
1373 	buf[i] = '\0';
1374 	return(buf);
1375 }
1376