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
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.
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]
22 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
23 /* All Rights Reserved */
28 * Copyright 1988-2003 Sun Microsystems, Inc. All rights reserved.
29 * Use is subject to license terms.
33 * Copyright 2012 Joyent, Inc. All rights reserved.
35 * Copyright (c) 2013 Gary Mills
42 #include <sys/types.h>
48 #include <sys/utsname.h>
58 #include <libzonecfg.h>
60 #include <sys/contract/process.h>
61 #include <libcontract.h>
65 * Use the full lengths from utmpx for user and line.
67 #define NMAX (sizeof (((struct utmpx *)0)->ut_user))
68 #define LMAX (sizeof (((struct utmpx *)0)->ut_line))
70 static char mesg
[3000];
73 static struct group
*pgrp
;
75 static char line
[MAXNAMLEN
+1] = "???";
76 static char systm
[MAXNAMLEN
+1];
78 static struct utsname utsn
;
79 static char who
[NMAX
+1] = "???";
80 static char time_buf
[50];
81 #define DATE_FMT "%a %b %e %H:%M:%S"
83 static void sendmes(struct utmpx
*, zoneid_t
);
84 static void sendmes_tozone(zoneid_t
, int);
85 static int chkgrp(char *);
86 static char *copy_str_till(char *, char *, char, int);
88 static int init_template(void);
89 int contract_abandon_id(ctid_t
);
92 main(int argc
, char *argv
[])
104 char *zonename
= NULL
;
105 zoneid_t
*zoneidlist
= NULL
;
106 uint_t nzids_saved
, nzids
= 0;
108 (void) setlocale(LC_ALL
, "");
110 while ((c
= getopt(argc
, argv
, "g:az:Z")) != EOF
)
117 (void) fprintf(stderr
,
118 "Only one group allowed\n");
121 if ((pgrp
= getgrnam(grpname
= optarg
)) == NULL
) {
122 (void) fprintf(stderr
, "Unknown group %s\n",
131 if (getzoneidbyname(zonename
) == -1) {
132 (void) fprintf(stderr
, "Specified zone %s "
133 "is invalid", zonename
);
146 (void) fprintf(stderr
,
147 "Usage: wall [-a] [-g group] [-z zone] [-Z] [files...]\n");
152 (void) fprintf(stderr
, "Cannot use -z with -Z\n");
157 infile
= argv
[optind
];
159 if (uname(&utsn
) == -1) {
160 (void) fprintf(stderr
, "wall: uname() failed, %s\n",
164 (void) strcpy(systm
, utsn
.nodename
);
167 * Get the name of the terminal wall is running from.
170 if ((term_name
= ttyname(fileno(stderr
))) != NULL
) {
172 * skip the leading "/dev/" in term_name
174 (void) strncpy(line
, &term_name
[5], sizeof (line
) - 1);
178 if (pwd
= getpwuid(getuid()))
179 (void) strncpy(&who
[0], pwd
->pw_name
, sizeof (who
));
184 f
= fopen(infile
, "r");
186 (void) fprintf(stderr
, "Cannot open %s\n", infile
);
193 while ((ptr
- start
) < 3000) {
196 if (fgets(ptr
, &mesg
[sizeof (mesg
)] - ptr
, f
) == NULL
)
198 if ((n
= strlen(ptr
)) == 0)
205 * If the request is from the rwall daemon then use the caller's
206 * name and host. We determine this if all of the following is true:
207 * 1) First 5 characters are "From "
208 * 2) Next non-white characters are of the form "name@host:"
210 if (strcmp(line
, "???") == 0) {
211 char rwho
[MAXNAMLEN
+1];
212 char rsystm
[MAXNAMLEN
+1];
215 if (strncmp(mesg
, "From ", 5) == 0) {
217 cp
= copy_str_till(rwho
, cp
, '@', MAXNAMLEN
+ 1);
218 if (rwho
[0] != '\0') {
219 cp
= copy_str_till(rsystm
, ++cp
, ':',
221 if (rsystm
[0] != '\0') {
222 (void) strcpy(systm
, rsystm
);
223 (void) strncpy(rwho
, who
,
225 (void) strcpy(line
, "rpc.rwalld");
231 (void) strftime(time_buf
, sizeof (time_buf
),
232 DATE_FMT
, localtime(&tloc
));
236 malloc(sizeof (zoneid_t
))) == NULL
||
237 (*zoneidlist
= getzoneidbyname(zonename
)) == -1)
240 } else if (Zflg
!= 0) {
241 if (zone_list(NULL
, &nzids
) != 0)
245 if ((zoneidlist
= malloc(nzids
* sizeof (zoneid_t
))) == NULL
)
248 if (zone_list(zoneidlist
, &nzids
) != 0) {
249 (void) free(zoneidlist
);
252 if (nzids
> nzids_saved
) {
258 for (; nzids
> 0; --nzids
)
259 sendmes_tozone(zoneidlist
[nzids
-1], aflag
);
262 sendmes_tozone(getzoneid(), aflag
);
268 * Copy src to destination upto but not including the delim.
269 * Leave dst empty if delim not found or whitespace encountered.
270 * Return pointer to next character (delim, whitespace, or '\0')
273 copy_str_till(char *dst
, char *src
, char delim
, int len
)
277 while (*src
!= '\0' && i
< len
) {
293 sendmes_tozone(zoneid_t zid
, int aflag
) {
295 char zonename
[ZONENAME_MAX
], root
[MAXPATHLEN
];
298 if (zid
!= getzoneid()) {
300 (void) getzonenamebyid(zid
, zonename
, ZONENAME_MAX
);
301 (void) zone_get_rootpath(zonename
, root
, sizeof (root
));
302 (void) strlcat(root
, UTMPX_FILE
, sizeof (root
));
303 if (!utmpxname(root
)) {
304 (void) fprintf(stderr
, "Cannot open %s\n", root
);
308 (void) utmpxname(UTMPX_FILE
);
311 while ((p
= getutxent()) != NULL
) {
312 if (p
->ut_type
!= USER_PROCESS
)
315 * if (-a option OR NOT pty window login), send the message
317 if (aflag
|| !nonuserx(*p
))
324 i
= (int)wait((int *)0);
325 } while (i
!= -1 || errno
!= ECHILD
);
330 * Note to future maintainers: with the change of wall to use the
331 * getutxent() API, the forked children (created by this function)
332 * must call _exit as opposed to exit. This is necessary to avoid
333 * unwanted fflushing of getutxent's stdio stream (caused by atexit
337 sendmes(struct utmpx
*p
, zoneid_t zid
)
341 static char device
[LMAX
+ 6];
346 boolean_t zoneenter
= B_FALSE
;
348 if (zid
!= getzoneid()) {
350 tmpl_fd
= init_template();
352 (void) fprintf(stderr
, "Could not initialize "
358 while ((i
= (int)fork()) == -1) {
360 (void) wait((int *)0);
367 if (zoneenter
&& zone_enter(zid
) == -1) {
368 char zonename
[ZONENAME_MAX
];
369 (void) getzonenamebyid(zid
, zonename
, ZONENAME_MAX
);
370 (void) fprintf(stderr
, "Could not enter zone "
374 (void) ct_tmpl_clear(tmpl_fd
);
377 if (!chkgrp(p
->ut_user
))
380 (void) signal(SIGHUP
, SIG_IGN
);
383 (void) snprintf(s
, sizeof (device
), "/dev/%.*s", LMAX
, p
->ut_line
);
385 /* check if the device is really a tty */
386 if ((fd
= open(s
, O_WRONLY
|O_NOCTTY
|O_NONBLOCK
)) == -1) {
387 (void) fprintf(stderr
, "Cannot send to %.*s on %s\n",
388 NMAX
, p
->ut_user
, s
);
390 (void) fflush(stderr
);
394 (void) fprintf(stderr
,
395 "Cannot send to device %.*s %s\n",
397 "because it's not a tty");
398 openlog("wall", 0, LOG_AUTH
);
399 syslog(LOG_CRIT
, "%.*s in utmpx is not a tty\n",
402 (void) fflush(stderr
);
408 f
= fopen("wall.debug", "a");
413 (void) fprintf(stderr
, "Cannot send to %-.*s on %s\n",
414 NMAX
, &p
->ut_user
[0], s
);
416 (void) fflush(stderr
);
420 "\07\07\07Broadcast Message from %s (%s) on %s %19.19s",
421 who
, line
, systm
, time_buf
);
423 (void) fprintf(f
, " to group %s", grpname
);
424 (void) fprintf(f
, "...\n");
426 (void) fprintf(f
, "DEBUG: To %.*s on %s\n", NMAX
, p
->ut_user
, s
);
429 for (bp
= mesg
; --i
>= 0; bp
++) {
430 ibp
= (unsigned int)((unsigned char) *bp
);
432 (void) putc('\r', f
);
433 if (isprint(ibp
) || *bp
== '\r' || *bp
== '\013' ||
434 *bp
== ' ' || *bp
== '\t' || *bp
== '\n' || *bp
== '\007') {
438 (void) fputs("M-", f
);
443 (void) putc(*bp
+ 0100, f
);
452 if (ferror(f
) || feof(f
)) {
453 (void) printf("\n\007Write failed\n");
454 (void) fflush(stdout
);
470 (void) strlcpy(user
, name
, sizeof (user
));
471 for (i
= 0; pgrp
->gr_mem
[i
] && pgrp
->gr_mem
[i
][0]; i
++) {
472 if (strcmp(user
, pgrp
->gr_mem
[i
]) == 0)
480 init_template(void) {
484 fd
= open64(CTFS_ROOT
"/process/template", O_RDWR
);
488 err
|= ct_tmpl_set_critical(fd
, 0);
489 err
|= ct_tmpl_set_informative(fd
, 0);
490 err
|= ct_pr_tmpl_set_fatal(fd
, CT_PR_EV_HWERR
);
491 err
|= ct_pr_tmpl_set_param(fd
, CT_PR_PGRPONLY
| CT_PR_REGENT
);
492 if (err
|| ct_tmpl_activate(fd
)) {