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
85char	*Nlsname;		/* set to argv[0]			*/
86char	Label[25];		/* label component for fmtmsg		*/
87int	Quietflag = FALSE;	/* set to TRUE when -q used		*/
88
89extern	int errno;
90void	nlsmesg();
91uid_t	geteuid();
92char	*nexttok();
93char	*pflags();
94char	*gencmdstr();
95
96struct	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
113void no_permission(void) __NORETURN;
114void usage(int flag);
115
116int
117main(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 = '\0';
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
471static 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
485void
486usage(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
508void
509no_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
521void
522nlsmesg(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
537int
538prt_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 = '\0';
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
582int
583old_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 = '\0';
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 */
661int
662prt_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") == 0 ||
697		    strcmp(state, "STARTING") == 0) {
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
728int
729prt_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
815int
816disable_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
855int
856enable_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
895int
896remove_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
936int
937kill_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
984int
985add_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
1031char *
1032gencmdstr(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
1049int
1050start_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
1127int
1128setup_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
1242int
1243svc_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++ = '\0';
1252	entry->pmtype = ptr;
1253	if ((ptr = strchr(entry->pmtype, ':')) == NULL)
1254		return(BADPMFMT);
1255	*ptr++ = '\0';
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++ = '\0';
1264	entry->flags = ptr;
1265	if ((ptr = strchr(entry->flags, ':')) == NULL)
1266		return(BADPMFMT);
1267	*ptr++ = '\0';
1268	entry->id = ptr;
1269	if ((ptr = strchr(entry->id, ':')) == NULL)
1270		return(BADPMFMT);
1271	*ptr++ = '\0';
1272	entry->res1 = ptr;
1273	if ((ptr = strchr(entry->res1, ':')) == NULL)
1274		return(BADPMFMT);
1275	*ptr++ = '\0';
1276	entry->res2 = ptr;
1277	if ((ptr = strchr(entry->res2, ':')) == NULL)
1278		return(BADPMFMT);
1279	*ptr++ = '\0';
1280	entry->res3 = ptr;
1281	if ((ptr = strchr(entry->res3, ':')) == NULL)
1282		return(BADPMFMT);
1283	*ptr++ = '\0';
1284	entry->addr = ptr;
1285	if ((ptr = strchr(entry->addr, ':')) == NULL)
1286		return(BADLISFMT);
1287	*ptr++ = '\0';
1288	entry->rpc = ptr;
1289	if ((ptr = strchr(entry->rpc, ':')) == NULL)
1290		return(BADLISFMT);
1291	*ptr++ = '\0';
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++ = '\0';
1301	entry->modules = ptr;
1302	if ((ptr = strchr(entry->modules, ':')) == NULL)
1303		return(BADLISFMT);
1304	*ptr++ = '\0';
1305	entry->command = ptr;
1306	if ((ptr = strchr(entry->command, '#')) == NULL)
1307		return(BADLISFMT);
1308	*ptr++ = '\0';
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
1324char *
1325nexttok(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
1351char *
1352pflags(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