just include fcntl.h not sys/fcntl.h
[oweals/busybox.git] / util-linux / ipcs.c
1 /*
2  * ipcs.c -- provides information on allocated ipc resources.
3  *
4  * 01 Sept 2004 - Rodney Radford <rradford@mindspring.com>
5  * Adapted for busybox from util-linux-2.12a.
6  *
7  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
8  */
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <getopt.h>
13 #include <errno.h>
14 #include <time.h>
15 #include <pwd.h>
16 #include <grp.h>
17
18 /* X/OPEN tells us to use <sys/{types,ipc,sem}.h> for semctl() */
19 /* X/OPEN tells us to use <sys/{types,ipc,msg}.h> for msgctl() */
20 /* X/OPEN tells us to use <sys/{types,ipc,shm}.h> for shmctl() */
21 #include <sys/types.h>
22 #include <sys/ipc.h>
23 #include <sys/sem.h>
24 #include <sys/msg.h>
25 #include <sys/shm.h>
26
27 #include "busybox.h"
28
29 /*-------------------------------------------------------------------*/
30 /* SHM_DEST and SHM_LOCKED are defined in kernel headers,
31    but inside #ifdef __KERNEL__ ... #endif */
32 #ifndef SHM_DEST
33 /* shm_mode upper byte flags */
34 #define SHM_DEST        01000   /* segment will be destroyed on last detach */
35 #define SHM_LOCKED      02000   /* segment will not be swapped */
36 #endif
37
38 /* For older kernels the same holds for the defines below */
39 #ifndef MSG_STAT
40 #define MSG_STAT        11
41 #define MSG_INFO        12
42 #endif
43
44 #ifndef SHM_STAT
45 #define SHM_STAT        13
46 #define SHM_INFO        14
47 struct shm_info {
48          int   used_ids;
49          ulong shm_tot; /* total allocated shm */
50          ulong shm_rss; /* total resident shm */
51          ulong shm_swp; /* total swapped shm */
52          ulong swap_attempts;
53          ulong swap_successes;
54 };
55 #endif
56
57 #ifndef SEM_STAT
58 #define SEM_STAT        18
59 #define SEM_INFO        19
60 #endif
61
62 /* Some versions of libc only define IPC_INFO when __USE_GNU is defined. */
63 #ifndef IPC_INFO
64 #define IPC_INFO        3
65 #endif
66 /*-------------------------------------------------------------------*/
67
68 /* The last arg of semctl is a union semun, but where is it defined?
69    X/OPEN tells us to define it ourselves, but until recently
70    Linux include files would also define it. */
71 #if defined (__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)
72 /* union semun is defined by including <sys/sem.h> */
73 #else
74 /* according to X/OPEN we have to define it ourselves */
75 union semun {
76         int val;
77         struct semid_ds *buf;
78         unsigned short int *array;
79         struct seminfo *__buf;
80 };
81 #endif
82
83 /* X/OPEN (Jan 1987) does not define fields key, seq in struct ipc_perm;
84    libc 4/5 does not mention struct ipc_term at all, but includes
85    <linux/ipc.h>, which defines a struct ipc_perm with such fields.
86    glibc-1.09 has no support for sysv ipc.
87    glibc 2 uses __key, __seq */
88 #if defined (__GNU_LIBRARY__) && __GNU_LIBRARY__ > 1
89 #define KEY __key
90 #else
91 #define KEY key
92 #endif
93
94 #define LIMITS 1
95 #define STATUS 2
96 #define CREATOR 3
97 #define TIME 4
98 #define PID 5
99
100
101 static void print_perms (int id, struct ipc_perm *ipcp) {
102         struct passwd *pw;
103         struct group *gr;
104
105         bb_printf ("%-10d %-10o", id, ipcp->mode & 0777);
106
107         if ((pw = getpwuid(ipcp->cuid)))
108                 bb_printf(" %-10s", pw->pw_name);
109         else
110                 bb_printf(" %-10d", ipcp->cuid);
111         if ((gr = getgrgid(ipcp->cgid)))
112                 bb_printf(" %-10s", gr->gr_name);
113         else
114                 bb_printf(" %-10d", ipcp->cgid);
115
116         if ((pw = getpwuid(ipcp->uid)))
117                 bb_printf(" %-10s", pw->pw_name);
118         else
119                 bb_printf(" %-10d", ipcp->uid);
120         if ((gr = getgrgid(ipcp->gid)))
121                 bb_printf(" %-10s\n", gr->gr_name);
122         else
123                 bb_printf(" %-10d\n", ipcp->gid);
124 }
125
126
127 static void do_shm (char format)
128 {
129         int maxid, shmid, id;
130         struct shmid_ds shmseg;
131         struct shm_info shm_info;
132         struct shminfo shminfo;
133         struct ipc_perm *ipcp = &shmseg.shm_perm;
134         struct passwd *pw;
135
136         maxid = shmctl (0, SHM_INFO, (struct shmid_ds *) (void *) &shm_info);
137         if (maxid < 0) {
138                 bb_printf ("kernel not configured for shared memory\n");
139                 return;
140         }
141
142         switch (format) {
143         case LIMITS:
144                 bb_printf ("------ Shared Memory Limits --------\n");
145                 if ((shmctl (0, IPC_INFO, (struct shmid_ds *) (void *) &shminfo)) < 0 )
146                         return;
147                 /* glibc 2.1.3 and all earlier libc's have ints as fields
148                    of struct shminfo; glibc 2.1.91 has unsigned long; ach */
149                 bb_printf ("max number of segments = %lu\n"
150                         "max seg size (kbytes) = %lu\n"
151                         "max total shared memory (pages) = %lu\n"
152                         "min seg size (bytes) = %lu\n",
153                         (unsigned long) shminfo.shmmni,
154                         (unsigned long) (shminfo.shmmax >> 10),
155                         (unsigned long) shminfo.shmall,
156                         (unsigned long) shminfo.shmmin);
157                 return;
158
159         case STATUS:
160                 bb_printf ("------ Shared Memory Status --------\n"
161                         "segments allocated %d\n"
162                         "pages allocated %ld\n"
163                         "pages resident  %ld\n"
164                         "pages swapped   %ld\n"
165                         "Swap performance: %ld attempts\t %ld successes\n",
166                         shm_info.used_ids,
167                         shm_info.shm_tot,
168                         shm_info.shm_rss,
169                         shm_info.shm_swp,
170                         shm_info.swap_attempts, shm_info.swap_successes);
171                 return;
172
173         case CREATOR:
174                 bb_printf ("------ Shared Memory Segment Creators/Owners --------\n"
175                         "%-10s %-10s %-10s %-10s %-10s %-10s\n",
176                         "shmid","perms","cuid","cgid","uid","gid");
177                 break;
178
179         case TIME:
180                 bb_printf ("------ Shared Memory Attach/Detach/Change Times --------\n"
181                         "%-10s %-10s %-20s %-20s %-20s\n",
182                         "shmid","owner","attached","detached","changed");
183                 break;
184
185         case PID:
186                 bb_printf ("------ Shared Memory Creator/Last-op --------\n"
187                         "%-10s %-10s %-10s %-10s\n",
188                         "shmid","owner","cpid","lpid");
189                 break;
190
191         default:
192                 bb_printf ("------ Shared Memory Segments --------\n"
193                         "%-10s %-10s %-10s %-10s %-10s %-10s %-12s\n",
194                         "key","shmid","owner","perms","bytes","nattch","status");
195                 break;
196         }
197
198         for (id = 0; id <= maxid; id++) {
199                 shmid = shmctl (id, SHM_STAT, &shmseg);
200                 if (shmid < 0)
201                         continue;
202                 if (format == CREATOR)  {
203                         print_perms (shmid, ipcp);
204                         continue;
205                 }
206                 pw = getpwuid(ipcp->uid);
207                 switch (format) {
208                 case TIME:
209                         if (pw)
210                                 bb_printf ("%-10d %-10.10s", shmid, pw->pw_name);
211                         else
212                                 bb_printf ("%-10d %-10d", shmid, ipcp->uid);
213                         /* ctime uses static buffer: use separate calls */
214                         bb_printf("  %-20.16s", shmseg.shm_atime
215                                    ? ctime(&shmseg.shm_atime) + 4 : "Not set");
216                         bb_printf(" %-20.16s", shmseg.shm_dtime
217                                    ? ctime(&shmseg.shm_dtime) + 4 : "Not set");
218                         bb_printf(" %-20.16s\n", shmseg.shm_ctime
219                                    ? ctime(&shmseg.shm_ctime) + 4 : "Not set");
220                         break;
221                 case PID:
222                         if (pw)
223                                 bb_printf ("%-10d %-10.10s", shmid, pw->pw_name);
224                         else
225                                 bb_printf ("%-10d %-10d", shmid, ipcp->uid);
226                         bb_printf (" %-10d %-10d\n",
227                                 shmseg.shm_cpid, shmseg.shm_lpid);
228                         break;
229
230                 default:
231                                 bb_printf("0x%08x ",ipcp->KEY );
232                         if (pw)
233                                 bb_printf ("%-10d %-10.10s", shmid, pw->pw_name);
234                         else
235                                 bb_printf ("%-10d %-10d", shmid, ipcp->uid);
236                         bb_printf ("%-10o %-10lu %-10ld %-6s %-6s\n",
237                                 ipcp->mode & 0777,
238                                 /*
239                                  * earlier: int, Austin has size_t
240                                  */
241                                 (unsigned long) shmseg.shm_segsz,
242                                 /*
243                                  * glibc-2.1.3 and earlier has unsigned short;
244                                  * Austin has shmatt_t
245                                  */
246                                 (long) shmseg.shm_nattch,
247                                 ipcp->mode & SHM_DEST ? "dest" : " ",
248                                 ipcp->mode & SHM_LOCKED ? "locked" : " ");
249                         break;
250                 }
251         }
252         return;
253 }
254
255
256 static void do_sem (char format)
257 {
258         int maxid, semid, id;
259         struct semid_ds semary;
260         struct seminfo seminfo;
261         struct ipc_perm *ipcp = &semary.sem_perm;
262         struct passwd *pw;
263         union semun arg;
264
265         arg.array = (ushort *)  (void *) &seminfo;
266         maxid = semctl (0, 0, SEM_INFO, arg);
267         if (maxid < 0) {
268                 bb_printf ("kernel not configured for semaphores\n");
269                 return;
270         }
271
272         switch (format) {
273         case LIMITS:
274                 bb_printf ("------ Semaphore Limits --------\n");
275                 arg.array = (ushort *) (void *) &seminfo; /* damn union */
276                 if ((semctl (0, 0, IPC_INFO, arg)) < 0 )
277                         return;
278                 bb_printf ("max number of arrays = %d\n"
279                         "max semaphores per array = %d\n"
280                         "max semaphores system wide = %d\n"
281                         "max ops per semop call = %d\n"
282                         "semaphore max value = %d\n",
283                         seminfo.semmni,
284                         seminfo.semmsl,
285                         seminfo.semmns,
286                         seminfo.semopm,
287                         seminfo.semvmx);
288                 return;
289
290         case STATUS:
291                 bb_printf ("------ Semaphore Status --------\n"
292                         "used arrays = %d\n"
293                         "allocated semaphores = %d\n",
294                         seminfo.semusz,
295                         seminfo.semaem);
296                 return;
297
298         case CREATOR:
299                 bb_printf ("------ Semaphore Arrays Creators/Owners --------\n"
300                         "%-10s %-10s %-10s %-10s %-10s %-10s\n",
301                         "semid","perms","cuid","cgid","uid","gid");
302                 break;
303
304         case TIME:
305                 bb_printf ("------ Shared Memory Operation/Change Times --------\n"
306                         "%-8s %-10s %-26.24s %-26.24s\n",
307                         "shmid","owner","last-op","last-changed");
308                 break;
309
310         case PID:
311                 break;
312
313         default:
314                 bb_printf ("------ Semaphore Arrays --------\n"
315                         "%-10s %-10s %-10s %-10s %-10s\n",
316                         "key","semid","owner","perms","nsems");
317                 break;
318         }
319
320         for (id = 0; id <= maxid; id++) {
321                 arg.buf = (struct semid_ds *) &semary;
322                 semid = semctl (id, 0, SEM_STAT, arg);
323                 if (semid < 0)
324                         continue;
325                 if (format == CREATOR)  {
326                         print_perms (semid, ipcp);
327                         continue;
328                 }
329                 pw = getpwuid(ipcp->uid);
330                 switch (format) {
331                 case TIME:
332                         if (pw)
333                                 bb_printf ("%-8d %-10.10s", semid, pw->pw_name);
334                         else
335                                 bb_printf ("%-8d %-10d", semid, ipcp->uid);
336                         bb_printf ("  %-26.24s", semary.sem_otime
337                                 ? ctime(&semary.sem_otime) : "Not set");
338                         bb_printf (" %-26.24s\n", semary.sem_ctime
339                                 ? ctime(&semary.sem_ctime) : "Not set");
340                         break;
341                 case PID:
342                         break;
343
344                 default:
345                                 bb_printf("0x%08x ", ipcp->KEY);
346                         if (pw)
347                                 bb_printf ("%-10d %-10.9s", semid, pw->pw_name);
348                         else
349                                 bb_printf ("%-10d %-9d", semid, ipcp->uid);
350                                         bb_printf ("%-10o %-10ld\n",
351                                 ipcp->mode & 0777,
352                                 /*
353                                  * glibc-2.1.3 and earlier has unsigned short;
354                                  * glibc-2.1.91 has variation between
355                                  * unsigned short and unsigned long
356                                  * Austin prescribes unsigned short.
357                                  */
358                                 (long) semary.sem_nsems);
359                         break;
360                 }
361         }
362 }
363
364
365 static void do_msg (char format)
366 {
367         int maxid, msqid, id;
368         struct msqid_ds msgque;
369         struct msginfo msginfo;
370         struct ipc_perm *ipcp = &msgque.msg_perm;
371         struct passwd *pw;
372
373         maxid = msgctl (0, MSG_INFO, (struct msqid_ds *) (void *) &msginfo);
374         if (maxid < 0) {
375                 bb_printf ("kernel not configured for message queues\n");
376                 return;
377         }
378
379         switch (format) {
380         case LIMITS:
381                 if ((msgctl (0, IPC_INFO, (struct msqid_ds *) (void *) &msginfo)) < 0 )
382                         return;
383                 bb_printf ("------ Messages: Limits --------\n"
384                         "max queues system wide = %d\n"
385                         "max size of message (bytes) = %d\n"
386                         "default max size of queue (bytes) = %d\n",
387                         msginfo.msgmni,
388                         msginfo.msgmax,
389                         msginfo.msgmnb);
390                 return;
391
392         case STATUS:
393                 bb_printf ("------ Messages: Status --------\n"
394                         "allocated queues = %d\n"
395                         "used headers = %d\n"
396                         "used space = %d bytes\n",
397                         msginfo.msgpool,
398                         msginfo.msgmap,
399                         msginfo.msgtql);
400                 return;
401
402         case CREATOR:
403                 bb_printf ("------ Message Queues: Creators/Owners --------\n"
404                         "%-10s %-10s %-10s %-10s %-10s %-10s\n",
405                         "msqid","perms","cuid","cgid","uid","gid");
406                 break;
407
408         case TIME:
409                 bb_printf ("------ Message Queues Send/Recv/Change Times --------\n"
410                         "%-8s %-10s %-20s %-20s %-20s\n",
411                         "msqid","owner","send","recv","change");
412                 break;
413
414         case PID:
415                 bb_printf ("------ Message Queues PIDs --------\n"
416                         "%-10s %-10s %-10s %-10s\n",
417                         "msqid","owner","lspid","lrpid");
418                 break;
419
420         default:
421                 bb_printf ("------ Message Queues --------\n"
422                         "%-10s %-10s %-10s %-10s %-12s %-12s\n",
423                         "key","msqid","owner","perms","used-bytes","messages");
424                 break;
425         }
426
427         for (id = 0; id <= maxid; id++) {
428                 msqid = msgctl (id, MSG_STAT, &msgque);
429                 if (msqid < 0)
430                         continue;
431                 if (format == CREATOR)  {
432                         print_perms (msqid, ipcp);
433                         continue;
434                 }
435                 pw = getpwuid(ipcp->uid);
436                 switch (format) {
437                 case TIME:
438                         if (pw)
439                                 bb_printf ("%-8d %-10.10s", msqid, pw->pw_name);
440                         else
441                                 bb_printf ("%-8d %-10d", msqid, ipcp->uid);
442                         bb_printf (" %-20.16s", msgque.msg_stime
443                                 ? ctime(&msgque.msg_stime) + 4 : "Not set");
444                         bb_printf (" %-20.16s", msgque.msg_rtime
445                                 ? ctime(&msgque.msg_rtime) + 4 : "Not set");
446                         bb_printf (" %-20.16s\n", msgque.msg_ctime
447                                 ? ctime(&msgque.msg_ctime) + 4 : "Not set");
448                         break;
449                 case PID:
450                         if (pw)
451                                 bb_printf ("%-8d %-10.10s", msqid, pw->pw_name);
452                         else
453                                 bb_printf ("%-8d %-10d", msqid, ipcp->uid);
454                         bb_printf ("  %5d     %5d\n",
455                                 msgque.msg_lspid, msgque.msg_lrpid);
456                         break;
457
458                 default:
459                                 bb_printf( "0x%08x ",ipcp->KEY );
460                         if (pw)
461                                 bb_printf ("%-10d %-10.10s", msqid, pw->pw_name);
462                         else
463                                 bb_printf ("%-10d %-10d", msqid, ipcp->uid);
464                                         bb_printf (" %-10o %-12ld %-12ld\n",
465                                 ipcp->mode & 0777,
466                                 /*
467                                  * glibc-2.1.3 and earlier has unsigned short;
468                                  * glibc-2.1.91 has variation between
469                                  * unsigned short, unsigned long
470                                  * Austin has msgqnum_t
471                                  */
472                                 (long) msgque.msg_cbytes,
473                                 (long) msgque.msg_qnum);
474                         break;
475                 }
476         }
477         return;
478 }
479
480
481 static void print_shm (int shmid)
482 {
483         struct shmid_ds shmds;
484         struct ipc_perm *ipcp = &shmds.shm_perm;
485
486         if (shmctl (shmid, IPC_STAT, &shmds) == -1) {
487                 perror ("shmctl ");
488                 return;
489         }
490
491         bb_printf ("\nShared memory Segment shmid=%d\n"
492                 "uid=%d\tgid=%d\tcuid=%d\tcgid=%d\n"
493                 "mode=%#o\taccess_perms=%#o\n"
494                 "bytes=%ld\tlpid=%d\tcpid=%d\tnattch=%ld\n"
495                 "att_time=%-26.24s\n"
496                 "det_time=%-26.24s\n"
497                 "change_time=%-26.24s\n"
498                 "\n",
499                 shmid,
500                 ipcp->uid, ipcp->gid, ipcp->cuid, ipcp->cgid,
501                 ipcp->mode, ipcp->mode & 0777,
502                 (long) shmds.shm_segsz, shmds.shm_lpid, shmds.shm_cpid,
503                 (long) shmds.shm_nattch,
504                 shmds.shm_atime ? ctime (&shmds.shm_atime) : "Not set",
505                 shmds.shm_dtime ? ctime (&shmds.shm_dtime) : "Not set",
506                 ctime (&shmds.shm_ctime));
507         return;
508 }
509
510
511 static void print_msg (int msqid)
512 {
513         struct msqid_ds buf;
514         struct ipc_perm *ipcp = &buf.msg_perm;
515
516         if (msgctl (msqid, IPC_STAT, &buf) == -1) {
517                 perror ("msgctl ");
518                 return;
519         }
520
521         bb_printf ("\nMessage Queue msqid=%d\n"
522                 "uid=%d\tgid=%d\tcuid=%d\tcgid=%d\tmode=%#o\n"
523                 "cbytes=%ld\tqbytes=%ld\tqnum=%ld\tlspid=%d\tlrpid=%d\n"
524                 "send_time=%-26.24s\n"
525                 "rcv_time=%-26.24s\n"
526                 "change_time=%-26.24s\n"
527                 "\n",
528                 msqid,
529                 ipcp->uid, ipcp->gid, ipcp->cuid, ipcp->cgid, ipcp->mode,
530                 /*
531                  * glibc-2.1.3 and earlier has unsigned short;
532                  * glibc-2.1.91 has variation between
533                  * unsigned short, unsigned long
534                  * Austin has msgqnum_t (for msg_qbytes)
535                  */
536                 (long) buf.msg_cbytes, (long) buf.msg_qbytes,
537                 (long) buf.msg_qnum, buf.msg_lspid, buf.msg_lrpid,
538                 buf.msg_stime ? ctime (&buf.msg_stime) : "Not set",
539                 buf.msg_rtime ? ctime (&buf.msg_rtime) : "Not set",
540                 buf.msg_ctime ? ctime (&buf.msg_ctime) : "Not set");
541         return;
542 }
543
544 static void print_sem (int semid)
545 {
546         struct semid_ds semds;
547         struct ipc_perm *ipcp = &semds.sem_perm;
548         union semun arg;
549         unsigned int i;
550
551         arg.buf = &semds;
552         if (semctl (semid, 0, IPC_STAT, arg) < 0) {
553                 perror ("semctl ");
554                 return;
555         }
556
557         bb_printf ("\nSemaphore Array semid=%d\n"
558                 "uid=%d\t gid=%d\t cuid=%d\t cgid=%d\n"
559                 "mode=%#o, access_perms=%#o\n"
560                 "nsems = %ld\n"
561                 "otime = %-26.24s\n"
562                 "ctime = %-26.24s\n"
563                 "%-10s %-10s %-10s %-10s %-10s\n",
564                 semid,
565                 ipcp->uid, ipcp->gid, ipcp->cuid, ipcp->cgid,
566                 ipcp->mode, ipcp->mode & 0777,
567                 (long) semds.sem_nsems,
568                 semds.sem_otime ? ctime (&semds.sem_otime) : "Not set",
569                 ctime (&semds.sem_ctime),
570                 "semnum","value","ncount","zcount","pid");
571
572         arg.val = 0;
573         for (i=0; i < semds.sem_nsems; i++) {
574                 int val, ncnt, zcnt, pid;
575                 val = semctl (semid, i, GETVAL, arg);
576                 ncnt = semctl (semid, i, GETNCNT, arg);
577                 zcnt = semctl (semid, i, GETZCNT, arg);
578                 pid = semctl (semid, i, GETPID, arg);
579                 if (val < 0 || ncnt < 0 || zcnt < 0 || pid < 0) {
580                         perror ("semctl ");
581                         bb_fflush_stdout_and_exit (1);
582                 }
583                 bb_printf ("%-10d %-10d %-10d %-10d %-10d\n",
584                         i, val, ncnt, zcnt, pid);
585         }
586         bb_printf ("\n");
587         return;
588 }
589
590 int ipcs_main (int argc, char **argv) {
591         int opt, msg = 0, sem = 0, shm = 0, id=0, print=0;
592         char format = 0;
593         char options[] = "atclupsmqi:ih?";
594
595         while ((opt = getopt (argc, argv, options)) != -1) {
596                 switch (opt) {
597                 case 'i':
598                         id = atoi (optarg);
599                         print = 1;
600                         break;
601                 case 'a':
602                         msg = shm = sem = 1;
603                         break;
604                 case 'q':
605                         msg = 1;
606                         break;
607                 case 's':
608                         sem = 1;
609                         break;
610                 case 'm':
611                         shm = 1;
612                         break;
613                 case 't':
614                         format = TIME;
615                         break;
616                 case 'c':
617                         format = CREATOR;
618                         break;
619                 case 'p':
620                         format = PID;
621                         break;
622                 case 'l':
623                         format = LIMITS;
624                         break;
625                 case 'u':
626                         format = STATUS;
627                         break;
628                 case 'h':
629                 case '?':
630                         bb_show_usage();
631                         bb_fflush_stdout_and_exit (0);
632                 }
633         }
634
635         if  (print) {
636                 if (shm) {
637                         print_shm (id);
638                         bb_fflush_stdout_and_exit (0);
639                 }
640                 if (sem) {
641                         print_sem (id);
642                         bb_fflush_stdout_and_exit (0);
643                 }
644                 if (msg) {
645                         print_msg (id);
646                         bb_fflush_stdout_and_exit (0);
647                 }
648                 bb_show_usage();
649                 bb_fflush_stdout_and_exit (0);
650         }
651
652         if ( !shm && !msg && !sem)
653                 msg = sem = shm = 1;
654         bb_printf ("\n");
655
656         if (shm) {
657                 do_shm (format);
658                 bb_printf ("\n");
659         }
660         if (sem) {
661                 do_sem (format);
662                 bb_printf ("\n");
663         }
664         if (msg) {
665                 do_msg (format);
666                 bb_printf ("\n");
667         }
668         return 0;
669 }
670