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