xref: /illumos-gate/usr/src/cmd/lp/lib/lp/alerts.c (revision 48bbca816818409505a6e214d0911fda44e622e3)
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 
43 extern char		**environ;
44 
45 static 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 
60 static 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 
86 int
87 putalert(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 
269 FALERT *
270 getalert(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 
385 int
386 delalert(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 
438 static void
439 envlist(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 
465 void
466 printalert(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