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 1997 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 * Copyright (c) 2016 by Delphix. All rights reserved.
26 */
27
28/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
29/*	  All Rights Reserved  	*/
30
31
32#pragma ident	"%Z%%M%	%I%	%E% SMI"	/* SVr4.0 1.17	*/
33/* EMACS_MODES: !fill, lnumb, !overwrite, !nodelete, !picture */
34
35#include "stdio.h"
36#include "string.h"
37#include "errno.h"
38#include "limits.h"
39#include "unistd.h"
40
41#include "lp.h"
42
43extern char		**environ;
44
45static void		envlist(int, char **);
46
47/*
48 * We recognize the following key phrases in the alert prototype
49 * file, and replace them with appropriate values.
50 */
51#define NALRT_KEYS	7
52# define ALRT_ENV		0
53# define ALRT_PWD		1
54# define ALRT_ULIMIT		2
55# define ALRT_UMASK		3
56# define ALRT_INTERVAL		4
57# define ALRT_CMD		5
58# define ALRT_USER		6
59
60static struct {
61	char			*v;
62	short			len;
63}			shell_keys[NALRT_KEYS] = {
64#define	ENTRY(X)	X, sizeof(X)-1
65	ENTRY("-ENVIRONMENT-"),
66	ENTRY("-PWD-"),
67	ENTRY("-ULIMIT-"),
68	ENTRY("-UMASK-"),
69	ENTRY("-INTERVAL-"),
70	ENTRY("-CMD-"),
71	ENTRY("-USER-"),
72};
73
74/*
75 * These are used to bracket the administrator's command, so that
76 * we can find it easily. We're out of luck if the administrator
77 * includes an identical phrase in their command.
78 */
79#define ALRT_CMDSTART "## YOUR COMMAND STARTS HERE -- DON'T TOUCH ABOVE!!"
80#define ALRT_CMDEND   "## YOUR COMMAND ENDS HERE -- DON'T TOUCH BELOW!!"
81
82/**
83 ** putalert() - WRITE ALERT TO FILES
84 **/
85
86int
87putalert(char *parent, char *name, FALERT *alertp)
88{
89	char			*path,
90				cur_dir[PATH_MAX + 1],
91				buf[BUFSIZ];
92
93	int			cur_umask;
94
95	int fdout, fdin;
96
97
98	if (!parent || !*parent || !name || !*name) {
99		errno = EINVAL;
100		return (-1);
101	}
102
103	if (!alertp->shcmd) {
104		errno = EINVAL;
105		return (-1);
106	}
107
108	if (STREQU(alertp->shcmd, NAME_NONE))
109		return (delalert(parent, name));
110
111	/*
112	 * See if the form/printer/print-wheel exists.
113	 */
114
115	if (!(path = makepath(parent, name, (char *)0)))
116		return (-1);
117
118	if (Access(path, F_OK) == -1) {
119		if (errno == ENOENT)
120			errno = ENOTDIR; /* not quite, but what else? */
121		Free (path);
122		return (-1);
123	}
124	Free (path);
125
126	/*
127	 * First, the shell command file.
128	 */
129
130	if (!(path = makepath(parent, name, ALERTSHFILE, (char *)0)))
131		return (-1);
132
133	if ((fdout = open_locked(path, "w", MODE_NOEXEC)) < 0) {
134		Free (path);
135		return (-1);
136	}
137	Free (path);
138
139	/*
140	 * We use a prototype file to build the shell command,
141	 * so that the alerts are easily customized. The shell
142	 * is expected to handle repeat alerts and failed alerts,
143	 * because the Spooler doesn't. Also, the Spooler runs
144	 * each alert with the UID and GID of the administrator
145	 * who defined the alert. Otherwise, anything goes.
146	 */
147
148	if (!Lp_Bin) {
149		getpaths ();
150		if (!Lp_Bin)
151			return (-1);
152	}
153	if (!(path = makepath(Lp_Bin, ALERTPROTOFILE, (char *)0)))
154		return (-1);
155
156	if ((fdin = open_locked(path, "r", 0)) < 0) {
157		Free (path);
158		return (-1);
159	}
160	Free (path);
161
162	errno = 0;
163	while (fdgets(buf, BUFSIZ, fdin)) {
164		int			key;
165		char			*cp,
166					*dash;
167
168		cp = buf;
169		while ((dash = strchr(cp, '-'))) {
170
171		    *dash = 0;
172		    fdputs (cp, fdout);
173		    *(cp = dash) = '-';
174
175		    for (key = 0; key < NALRT_KEYS; key++)
176			if (STRNEQU(
177				cp,
178				shell_keys[key].v,
179				shell_keys[key].len
180			)) {
181				register char	*newline =
182						(cp != buf)? "\n" : "";
183
184				cp += shell_keys[key].len;
185
186				switch (key) {
187
188				case ALRT_ENV:
189					fdprintf(fdout, newline);
190					envlist(fdout, environ);
191					break;
192
193				case ALRT_PWD:
194					getcwd (cur_dir, PATH_MAX);
195					fdprintf (fdout, "%s", cur_dir);
196					break;
197
198				case ALRT_ULIMIT:
199					fdprintf (fdout, "%ld", ulimit(1, (long)0));
200					break;
201
202				case ALRT_UMASK:
203					umask (cur_umask = umask(0));
204					fdprintf (fdout, "%03o", cur_umask);
205					break;
206
207				case ALRT_INTERVAL:
208					fdprintf(fdout, "%ld", (long)alertp->W);
209					break;
210
211				case ALRT_CMD:
212					fdprintf(fdout, newline);
213					fdprintf(fdout, "%s\n", ALRT_CMDSTART);
214					fdprintf(fdout, "%s\n", alertp->shcmd);
215					fdprintf(fdout, "%s\n", ALRT_CMDEND);
216					break;
217
218				case ALRT_USER:
219					fdprintf(fdout, "%s", getname());
220					break;
221
222				}
223
224				break;
225			}
226		    if (key >= NALRT_KEYS)
227			fdputc(*cp++, fdout);
228
229		}
230		fdputs(cp, fdout);
231
232	}
233	if (errno != 0) {
234		int			save_errno = errno;
235
236		close(fdin);
237		close(fdout);
238		errno = save_errno;
239		return (-1);
240	}
241	close(fdin);
242	close(fdout);
243
244	/*
245	 * Next, the variables file.
246	 */
247
248	if (!(path = makepath(parent, name, ALERTVARSFILE, (char *)0)))
249		return (-1);
250
251	if ((fdout = open_locked(path, "w", MODE_NOREAD)) < 0) {
252		Free (path);
253		return (-1);
254	}
255	Free (path);
256
257	fdprintf(fdout, "%d\n", alertp->Q > 0? alertp->Q : 1);
258	fdprintf(fdout, "%d\n", alertp->W >= 0? alertp->W : 0);
259
260	close(fdout);
261
262	return (0);
263}
264
265/**
266 ** getalert() - EXTRACT ALERT FROM FILES
267 **/
268
269FALERT *
270getalert(char *parent, char *name)
271{
272	int fd;
273	char *tmp;
274	static FALERT		alert;
275	register char		*path;
276	char			buf[BUFSIZ];
277	int			len;
278
279	if (!parent || !*parent || !name || !*name) {
280		errno = EINVAL;
281		return (0);
282	}
283
284	/*
285	 * See if the form/printer/print-wheel exists.
286	 */
287
288	if (!(path = makepath(parent, name, (char *)0)))
289		return (0);
290
291	if (Access(path, F_OK) == -1) {
292		if (errno == ENOENT)
293			errno = ENOTDIR; /* not quite, but what else? */
294		Free (path);
295		return (0);
296	}
297	Free (path);
298
299	/*
300	 * First, the shell command file.
301	 */
302
303	if (!(path = makepath(parent, name, ALERTSHFILE, (char *)0)))
304		return (0);
305
306	if ((fd = open_locked(path, "r", 0)) < 0) {
307		Free (path);
308		return (0);
309	}
310	Free (path);
311
312	/*
313	 * Skip over environment setting stuff, while loop, etc.,
314	 * to find the beginning of the command.
315	 */
316	errno = 0;
317	while ((tmp =  fdgets(buf, BUFSIZ, fd)) &&
318		!STRNEQU(buf, ALRT_CMDSTART, sizeof(ALRT_CMDSTART)-1))
319		;
320	if ((tmp == NULL) || (errno != 0)) {
321		int			save_errno = errno;
322
323		close(fd);
324		errno = save_errno;
325		return (0);
326	}
327
328	alert.shcmd = sop_up_rest(fd, ALRT_CMDEND);
329
330	close(fd);
331
332	if (!alert.shcmd)
333		return (0);
334
335	/*
336	 * Drop terminating newline.
337	 */
338	if (alert.shcmd[(len = strlen(alert.shcmd)) - 1] == '\n')
339		alert.shcmd[len - 1] = 0;
340
341
342	/*
343	 * Next, the variables file.
344	 */
345
346	if (!(path = makepath(parent, name, ALERTVARSFILE, (char *)0)))
347		return (0);
348
349	if ((fd = open_locked(path, "r", 0)) < 0) {
350		Free (path);
351		return (0);
352	}
353	Free (path);
354
355	errno = 0;
356	(void)fdgets (buf, BUFSIZ, fd);
357	if (errno != 0) {
358		int			save_errno = errno;
359
360		close(fd);
361		errno = save_errno;
362		return (0);
363	}
364	alert.Q = atoi(buf);
365
366	(void)fdgets (buf, BUFSIZ, fd);
367	if (errno != 0) {
368		int			save_errno = errno;
369
370		close(fd);
371		errno = save_errno;
372		return (0);
373	}
374	alert.W = atoi(buf);
375
376	close(fd);
377
378	return (&alert);
379}
380
381/**
382 ** delalert() - DELETE ALERT FILES
383 **/
384
385int
386delalert(char *parent, char *name)
387{
388	char			*path;
389
390
391	if (!parent || !*parent || !name || !*name) {
392		errno = EINVAL;
393		return (-1);
394	}
395
396	/*
397	 * See if the form/printer/print-wheel exists.
398	 */
399
400	if (!(path = makepath(parent, name, (char *)0)))
401		return (-1);
402
403	if (Access(path, F_OK) == -1) {
404		if (errno == ENOENT)
405			errno = ENOTDIR; /* not quite, but what else? */
406		Free (path);
407		return (-1);
408	}
409	Free (path);
410
411	/*
412	 * Remove the two files.
413	 */
414
415	if (!(path = makepath(parent, name, ALERTSHFILE, (char *)0)))
416		return (-1);
417	if (rmfile(path) == -1) {
418		Free (path);
419		return (-1);
420	}
421	Free (path);
422
423	if (!(path = makepath(parent, name, ALERTVARSFILE, (char *)0)))
424		return (-1);
425	if (rmfile(path) == -1) {
426		Free (path);
427		return (-1);
428	}
429	Free (path);
430
431	return (0);
432}
433
434/**
435 ** envlist() - PRINT OUT ENVIRONMENT LIST SAFELY
436 **/
437
438static void
439envlist(int fd, char **list)
440{
441	register char		*env,
442				*value;
443
444	if (!list || !*list)
445		return;
446
447	while ((env = *list++)) {
448		if (!(value = strchr(env, '=')))
449			continue;
450		*value++ = 0;
451		if (!strchr(value, '\''))
452			fdprintf(fd, (char *)gettext("export %s; %s='%s'\n"),
453				env, env, value);
454		*--value = '=';
455	}
456}
457
458/*
459 * printalert() - PRINT ALERT DESCRIPTION
460 *
461 * This is not used in the scheduler, so we don't need to switch to using
462 * file descriptors for scalability.
463 */
464
465void
466printalert(FILE *fp, FALERT *alertp, int isfault)
467{
468	if (!alertp->shcmd) {
469		if (isfault)
470			(void)fprintf (fp, (char *)gettext("On fault: no alert\n"));
471		else
472			(void)fprintf (fp, (char *)gettext("No alert\n"));
473
474	} else {
475		register char	*copy = Strdup(alertp->shcmd),
476				*cp;
477
478		if (isfault)
479			(void)fprintf (fp, (char *)gettext("On fault: "));
480		else
481			if (alertp->Q > 1)
482				(void)fprintf (
483					fp,
484					(char *)gettext("When %d are queued: "),
485					alertp->Q
486				);
487			else
488				(void)fprintf (fp, (char *)gettext("Upon any being queued: "));
489
490		if (copy && (cp = strchr(copy, ' ')))
491			while (*cp == ' ')
492				*cp++ = 0;
493
494		if (
495			copy
496		     && syn_name(cp)
497		     && (
498				STREQU(copy, NAME_WRITE)
499			     || STREQU(copy, NAME_MAIL)
500			)
501		)
502			(void)fprintf (fp, "%s to %s ", copy, cp);
503		else
504			(void)fprintf (fp, (char *)gettext("alert with \"%s\" "), alertp->shcmd);
505
506		if (alertp->W > 0)
507			(void)fprintf (fp, (char *)gettext("every %d minutes\n"), alertp->W);
508		else
509			(void)fprintf (fp, (char *)gettext("once\n"));
510
511		Free (copy);
512	}
513	return;
514}
515