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