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