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