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