aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS3
-rw-r--r--docs/busybox_header.pod18
-rw-r--r--include/applets.h6
-rw-r--r--include/usage.h42
-rw-r--r--sysdeps/linux/defconfig2
-rw-r--r--util-linux/Config.in45
-rw-r--r--util-linux/Makefile.in2
-rw-r--r--util-linux/ipcrm.c273
-rw-r--r--util-linux/ipcs.c686
9 files changed, 1049 insertions, 28 deletions
diff --git a/AUTHORS b/AUTHORS
index 7e773020f..13fb781db 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -113,6 +113,9 @@ Bruce Perens <bruce@pixar.com>
113 Original author of BusyBox in 1995, 1996. Some of his code can 113 Original author of BusyBox in 1995, 1996. Some of his code can
114 still be found hiding here and there... 114 still be found hiding here and there...
115 115
116Rodney Radford <rradford@mindspring.com>
117 ipcs, ipcrm
118
116Tim Riker <Tim@Rikers.org> 119Tim Riker <Tim@Rikers.org>
117 bug fixes, member of fan club 120 bug fixes, member of fan club
118 121
diff --git a/docs/busybox_header.pod b/docs/busybox_header.pod
index 35631b84e..5af92401c 100644
--- a/docs/busybox_header.pod
+++ b/docs/busybox_header.pod
@@ -89,15 +89,15 @@ Currently defined functions include:
89 fgrep, find, fold, free, freeramdisk, fsck.minix, ftpget, ftpput, getopt, 89 fgrep, find, fold, free, freeramdisk, fsck.minix, ftpget, ftpput, getopt,
90 getty, grep, gunzip, gzip, halt, hdparm, head, hexdump, hostid, hostname, 90 getty, grep, gunzip, gzip, halt, hdparm, head, hexdump, hostid, hostname,
91 httpd, hush, hwclock, id, ifconfig, ifdown, ifup, inetd, init, insmod, 91 httpd, hush, hwclock, id, ifconfig, ifdown, ifup, inetd, init, insmod,
92 install, ip, ipaddr, ipcalc, iplink, iproute, iptunnel, kill, killall, 92 install, ip, ipaddr, ipcalc, ipcrm, ipcs, iplink, iproute, iptunnel, kill,
93 klogd, lash, last, length, linuxrc, ln, loadfont, loadkmap, logger, login, 93 killall, klogd, lash, last, length, linuxrc, ln, loadfont, loadkmap,
94 logname, logread, losetup, ls, lsmod, makedevs, md5sum, mesg, mkdir, 94 logger, login, logname, logread, losetup, ls, lsmod, makedevs, md5sum,
95 mkfifo, mkfs.minix, mknod, mkswap, mktemp, modprobe, more, mount, msh, mt, 95 mesg, mkdir, mkfifo, mkfs.minix, mknod, mkswap, mktemp, modprobe, more,
96 mv, nameif, nc, netstat, nslookup, od, openvt, passwd, patch, pidof, ping, 96 mount, msh, mt, mv, nameif, nc, netstat, nslookup, od, openvt, passwd,
97 ping6, pipe_progress, pivot_root, poweroff, printf, ps, pwd, rdate, 97 patch, pidof, ping, ping6, pipe_progress, pivot_root, poweroff, printf, ps,
98 readlink, realpath, reboot, renice, reset, rm, rmdir, rmmod, route, rpm, 98 pwd, rdate, readlink, realpath, reboot, renice, reset, rm, rmdir, rmmod,
99 rpm2cpio, run-parts, rx, sed, seq, setkeycodes, sha1sum, sleep, sort, 99 route, rpm, rpm2cpio, run-parts, rx, sed, seq, setkeycodes, sha1sum, sleep,
100 start-stop-daemon, strings, stty, su, sulogin, swapoff, swapon, sync, 100 sort, start-stop-daemon, strings, stty, su, sulogin, swapoff, swapon, sync,
101 sysctl, syslogd, tail, tar, tee, telnet, telnetd, test, tftp, time, top, 101 sysctl, syslogd, tail, tar, tee, telnet, telnetd, test, tftp, time, top,
102 touch, tr, traceroute, true, tty, udhcpc, udhcpd, umount, uname, 102 touch, tr, traceroute, true, tty, udhcpc, udhcpd, umount, uname,
103 uncompress, uniq, unix2dos, unzip, uptime, usleep, uudecode, uuencode, 103 uncompress, uniq, unix2dos, unzip, uptime, usleep, uudecode, uuencode,
diff --git a/include/applets.h b/include/applets.h
index e0b18085a..a7578ba1a 100644
--- a/include/applets.h
+++ b/include/applets.h
@@ -320,6 +320,12 @@
320#ifdef CONFIG_IPCALC 320#ifdef CONFIG_IPCALC
321 APPLET(ipcalc, ipcalc_main, _BB_DIR_BIN, _BB_SUID_NEVER) 321 APPLET(ipcalc, ipcalc_main, _BB_DIR_BIN, _BB_SUID_NEVER)
322#endif 322#endif
323#ifdef CONFIG_IPCRM
324 APPLET(ipcrm, ipcrm_main, _BB_DIR_USR_BIN, _BB_SUID_ALWAYS)
325#endif
326#ifdef CONFIG_IPCS
327 APPLET(ipcs, ipcs_main, _BB_DIR_USR_BIN, _BB_SUID_ALWAYS)
328#endif
323#ifdef CONFIG_IPLINK 329#ifdef CONFIG_IPLINK
324 APPLET(iplink, iplink_main, _BB_DIR_BIN, _BB_SUID_NEVER) 330 APPLET(iplink, iplink_main, _BB_DIR_BIN, _BB_SUID_NEVER)
325#endif 331#endif
diff --git a/include/usage.h b/include/usage.h
index 5fb9858bc..017cb9c3e 100644
--- a/include/usage.h
+++ b/include/usage.h
@@ -1395,6 +1395,32 @@
1395 "\t-h\t--hostname\tDisplay first resolved host name\n" \ 1395 "\t-h\t--hostname\tDisplay first resolved host name\n" \
1396 "\t-s\t--silent\tDon't ever display error messages.") 1396 "\t-s\t--silent\tDon't ever display error messages.")
1397 1397
1398#define ipcrm_trivial_usage \
1399 "[-[MQS] key] [-[mqs] id]"
1400#define ipcrm_full_usage \
1401 "The upper-case options MQS are used to remove a shared memory\n" \
1402 "segment by an shmkey value. The lower-case options mqs are used\n" \
1403 "to remove a segment by shmid value.\n" \
1404 "\t-m | -M\tRemove the memory segment after the last detatch\n" \
1405 "\t-q | -Q\tRemove the message queue\n" \
1406 "\t-s | -S\tRemove the semaphore\n"
1407
1408#define ipcs_trivial_usage \
1409 "[[-smq] -i shmid] | [[-asmq] [-tclup]]"
1410#define ipcs_full_usage \
1411 "\t-i\tspecify a specific resource id\n" \
1412 "Resource specification:\n" \
1413 "\t-m\tshared memory segments\n" \
1414 "\t-q\tmessage queues\n" \
1415 "\t-s\tsempahore arrays\n" \
1416 "\t-a\tall (default)\n" \
1417 "Output format:\n" \
1418 "\t-t\ttime\n" \
1419 "\t-p\tpid\n" \
1420 "\t-s\tcreator\n" \
1421 "\t-a\tlimits\n" \
1422 "\t-i\tsummary\n"
1423
1398#define iplink_trivial_usage \ 1424#define iplink_trivial_usage \
1399 "{ set DEVICE { up | down | arp { on | off } | show [ DEVICE ] }" 1425 "{ set DEVICE { up | down | arp { on | off } | show [ DEVICE ] }"
1400#define iplink_full_usage \ 1426#define iplink_full_usage \
@@ -2080,18 +2106,30 @@
2080 "$ printf \"Val=%d\\n\" 5\n" \ 2106 "$ printf \"Val=%d\\n\" 5\n" \
2081 "Val=5\n" 2107 "Val=5\n"
2082 2108
2109#if !defined(CONFIG_SELINUX) && !defined(CONFIG_PS_FEATURE_WIDE)
2110#define USAGE_PS "\n\tThis version of ps accepts no options."
2111#else
2112#define USAGE_PS "\nOptions:"
2113#endif
2083#ifdef CONFIG_SELINUX 2114#ifdef CONFIG_SELINUX
2084#define USAGE_NONSELINUX(a) 2115#define USAGE_NONSELINUX(a)
2085#else 2116#else
2086#define USAGE_NONSELINUX(a) a 2117#define USAGE_NONSELINUX(a) a
2087#endif 2118#endif
2119#ifdef CONFIG_PS_FEATURE_WIDE
2120#define USAGE_PS_WIDE(a) a
2121#else
2122#define USAGE_PS_WIDE(a)
2123#endif
2088 2124
2089#define ps_trivial_usage \ 2125#define ps_trivial_usage \
2090 "" 2126 ""
2091#define ps_full_usage \ 2127#define ps_full_usage \
2092 "Report process status\n" \ 2128 "Report process status\n" \
2093 USAGE_NONSELINUX("\n\tThis version of ps accepts no options.") \ 2129 USAGE_PS \
2094 USAGE_SELINUX("\nOptions:\n\t-c\tshow SE Linux context") 2130 USAGE_SELINUX("\n\t-c\tshow SE Linux context") \
2131 USAGE_PS_WIDE("\n\tw\twide output")
2132
2095 2133
2096#define ps_example_usage \ 2134#define ps_example_usage \
2097 "$ ps\n" \ 2135 "$ ps\n" \
diff --git a/sysdeps/linux/defconfig b/sysdeps/linux/defconfig
index fd2b118e3..8848a6c37 100644
--- a/sysdeps/linux/defconfig
+++ b/sysdeps/linux/defconfig
@@ -397,6 +397,8 @@ CONFIG_DMESG=y
397# CONFIG_GETOPT is not set 397# CONFIG_GETOPT is not set
398CONFIG_HEXDUMP=y 398CONFIG_HEXDUMP=y
399# CONFIG_HWCLOCK is not set 399# CONFIG_HWCLOCK is not set
400# CONFIG_IPCRM is not set
401# CONFIG_IPCS is not set
400# CONFIG_LOSETUP is not set 402# CONFIG_LOSETUP is not set
401# CONFIG_MKSWAP is not set 403# CONFIG_MKSWAP is not set
402CONFIG_MORE=y 404CONFIG_MORE=y
diff --git a/util-linux/Config.in b/util-linux/Config.in
index 3c990b936..a1e0b0848 100644
--- a/util-linux/Config.in
+++ b/util-linux/Config.in
@@ -216,6 +216,21 @@ config CONFIG_LOSETUP
216 file or block device, and to query the status of a loop device. This 216 file or block device, and to query the status of a loop device. This
217 version does not currently support enabling data encryption. 217 version does not currently support enabling data encryption.
218 218
219config CONFIG_IPCRM
220 bool "ipcrm"
221 default n
222 help
223 The ipcrm utility allows the removal of System V interprocess
224 communication (IPC) objects and the associated data structures
225 from the system.
226
227config CONFIG_IPCS
228 bool "ipcs"
229 default n
230 help
231 The ipcs utility is used to provide information on the currently
232 allocated System V interprocess (IPC) objects in the system.
233
219config CONFIG_MKSWAP 234config CONFIG_MKSWAP
220 bool "mkswap" 235 bool "mkswap"
221 default n 236 default n
@@ -322,14 +337,18 @@ config CONFIG_FEATURE_MOUNT_LOOP
322 default n 337 default n
323 depends on CONFIG_MOUNT || CONFIG_UMOUNT 338 depends on CONFIG_MOUNT || CONFIG_UMOUNT
324 help 339 help
325 Enabling this feature allows mount to use the '-o' loop options, 340 Enabling this feature allows automatic loopback mounts, meaning you can mount
326 which lets you loop mount files. Mount will automagically setup and 341 filesystems contained in normal files as well as in block devices. The mount
327 free the necessary loop devices so you do not need to mess with the 342 and umount commands will detect you are trying to mount a file instead of a
328 'losetup' utility unless you really want to. This is really 343 block device, and transparently associate it with a loopback device (and free
329 only useful if you plan to loop mount files. 344 the loopback device on unmount) for you.
345
346 You can still use the 'losetup' utility and mount the loopback device yourself
347 if you need to do something advanced, such as specify an offset or cryptographic
348 options to the loopback device.
330 349
331config CONFIG_FEATURE_MTAB_SUPPORT 350config CONFIG_FEATURE_MTAB_SUPPORT
332 bool " Support for a real /etc/mtab (instead of /proc/mounts)" 351 bool " Support for a /etc/mtab file (instead of symlink to /proc/mounts)"
333 default n 352 default n
334 depends on CONFIG_MOUNT || CONFIG_UMOUNT 353 depends on CONFIG_MOUNT || CONFIG_UMOUNT
335 help 354 help
@@ -339,17 +358,9 @@ config CONFIG_FEATURE_MTAB_SUPPORT
339 BusyBox have a read-only root filesystem, so they will leave this 358 BusyBox have a read-only root filesystem, so they will leave this
340 option disabled and BusyBox will use the /proc/mounts file. 359 option disabled and BusyBox will use the /proc/mounts file.
341 360
342config CONFIG_FEATURE_MTAB_FILENAME 361 Note that even non-embedded developers probably want to have /etc/mtab
343 string " mtab file location" 362 be a symlink to /proc/mounts, since otherwise mtab can get out of sync
344 default "/etc/mtab" 363 with the real kernel mount state in numerous ways.
345 depends on CONFIG_FEATURE_MTAB_SUPPORT
346 help
347 Some people have a read only root filesystem, but they also wish to
348 have the 'mount' utility create an mtab file listing the filesystems
349 which have been mounted. This option allows you to specify an alternative
350 location for the mtab file, such as /var/mtab, or /tmp/mtab. The default
351 value is /etc/mtab, which is where this file is located on most desktop
352 Linux systems.
353 364
354config CONFIG_READPROFILE 365config CONFIG_READPROFILE
355 bool "readprofile" 366 bool "readprofile"
diff --git a/util-linux/Makefile.in b/util-linux/Makefile.in
index 9aaaf9f8f..504560c27 100644
--- a/util-linux/Makefile.in
+++ b/util-linux/Makefile.in
@@ -34,6 +34,8 @@ UTILLINUX-$(CONFIG_FSCK_MINIX) +=fsck_minix.o
34UTILLINUX-$(CONFIG_GETOPT) +=getopt.o 34UTILLINUX-$(CONFIG_GETOPT) +=getopt.o
35UTILLINUX-$(CONFIG_HEXDUMP) +=hexdump.o 35UTILLINUX-$(CONFIG_HEXDUMP) +=hexdump.o
36UTILLINUX-$(CONFIG_HWCLOCK) +=hwclock.o 36UTILLINUX-$(CONFIG_HWCLOCK) +=hwclock.o
37UTILLINUX-$(CONFIG_IPCRM) +=ipcrm.o
38UTILLINUX-$(CONFIG_IPCS) +=ipcs.o
37UTILLINUX-$(CONFIG_LOSETUP) +=losetup.o 39UTILLINUX-$(CONFIG_LOSETUP) +=losetup.o
38UTILLINUX-$(CONFIG_MKFS_MINIX) +=mkfs_minix.o 40UTILLINUX-$(CONFIG_MKFS_MINIX) +=mkfs_minix.o
39UTILLINUX-$(CONFIG_MKSWAP) +=mkswap.o 41UTILLINUX-$(CONFIG_MKSWAP) +=mkswap.o
diff --git a/util-linux/ipcrm.c b/util-linux/ipcrm.c
new file mode 100644
index 000000000..9e18752a3
--- /dev/null
+++ b/util-linux/ipcrm.c
@@ -0,0 +1,273 @@
1/*
2 * ipcrm.c -- utility to allow removal of IPC objects and data structures.
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-04-02 frank zago
24 * - can now remove several id's in the same call
25 *
26 * 1999-02-22 Arkadiusz Miÿkiewicz <misiek@pld.ORG.PL>
27 * - added Native Language Support
28 *
29 * Original author - krishna balasubramanian 1993
30 */
31
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35#include <errno.h>
36
37#include <sys/types.h>
38#include <sys/ipc.h>
39#include <sys/shm.h>
40#include <sys/msg.h>
41#include <sys/sem.h>
42
43/* X/OPEN tells us to use <sys/{types,ipc,sem}.h> for semctl() */
44/* X/OPEN tells us to use <sys/{types,ipc,msg}.h> for msgctl() */
45/* for getopt */
46#include <unistd.h>
47
48/* for tolower and isupper */
49#include <ctype.h>
50
51#include "busybox.h"
52
53#if defined (__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)
54/* union semun is defined by including <sys/sem.h> */
55#else
56/* according to X/OPEN we have to define it ourselves */
57union semun {
58 int val;
59 struct semid_ds *buf;
60 unsigned short int *array;
61 struct seminfo *__buf;
62};
63#endif
64
65typedef enum type_id {
66 SHM,
67 SEM,
68 MSG
69} type_id;
70
71static int
72remove_ids(type_id type, int argc, char **argv) {
73 int id;
74 int ret = 0; /* for gcc */
75 char *end;
76 int nb_errors = 0;
77 union semun arg;
78
79 arg.val = 0;
80
81 while(argc) {
82
83 id = strtoul(argv[0], &end, 10);
84
85 if (*end != 0) {
86 bb_printf ("invalid id: %s\n", argv[0]);
87 nb_errors ++;
88 } else {
89 switch(type) {
90 case SEM:
91 ret = semctl (id, 0, IPC_RMID, arg);
92 break;
93
94 case MSG:
95 ret = msgctl (id, IPC_RMID, NULL);
96 break;
97
98 case SHM:
99 ret = shmctl (id, IPC_RMID, NULL);
100 break;
101 }
102
103 if (ret) {
104 bb_printf ("cannot remove id %s (%s)\n",
105 argv[0], strerror(errno));
106 nb_errors ++;
107 }
108 }
109 argc--;
110 argv++;
111 }
112
113 return(nb_errors);
114}
115
116static int deprecated_main(int argc, char **argv)
117{
118 if (argc < 3) {
119 bb_show_usage();
120 bb_fflush_stdout_and_exit(1);
121 }
122
123 if (!strcmp(argv[1], "shm")) {
124 if (remove_ids(SHM, argc-2, &argv[2]))
125 bb_fflush_stdout_and_exit(1);
126 }
127 else if (!strcmp(argv[1], "msg")) {
128 if (remove_ids(MSG, argc-2, &argv[2]))
129 bb_fflush_stdout_and_exit(1);
130 }
131 else if (!strcmp(argv[1], "sem")) {
132 if (remove_ids(SEM, argc-2, &argv[2]))
133 bb_fflush_stdout_and_exit(1);
134 }
135 else {
136 bb_printf ("unknown resource type: %s\n", argv[1]);
137 bb_show_usage();
138 bb_fflush_stdout_and_exit(1);
139 }
140
141 bb_printf ("resource(s) deleted\n");
142 return 0;
143}
144
145
146int ipcrm_main(int argc, char **argv)
147{
148 int c;
149 int error = 0;
150 char *prog = argv[0];
151
152 /* if the command is executed without parameters, do nothing */
153 if (argc == 1)
154 return 0;
155
156 /* check to see if the command is being invoked in the old way if so
157 then run the old code */
158 if (strcmp(argv[1], "shm") == 0 ||
159 strcmp(argv[1], "msg") == 0 ||
160 strcmp(argv[1], "sem") == 0)
161 return deprecated_main(argc, argv);
162
163 /* process new syntax to conform with SYSV ipcrm */
164 while ((c = getopt(argc, argv, "q:m:s:Q:M:S:h?")) != -1) {
165 int result;
166 int id = 0;
167 int iskey = isupper(c);
168
169 /* needed to delete semaphores */
170 union semun arg;
171 arg.val = 0;
172
173 if ((c == '?') || (c == 'h'))
174 {
175 bb_show_usage();
176 return 0;
177 }
178
179 /* we don't need case information any more */
180 c = tolower(c);
181
182 /* make sure the option is in range */
183 if (c != 'q' && c != 'm' && c != 's') {
184 bb_show_usage();
185 error++;
186 return error;
187 }
188
189 if (iskey) {
190 /* keys are in hex or decimal */
191 key_t key = strtoul(optarg, NULL, 0);
192 if (key == IPC_PRIVATE) {
193 error++;
194 bb_fprintf(stderr, "%s: illegal key (%s)\n",
195 prog, optarg);
196 continue;
197 }
198
199 /* convert key to id */
200 id = ((c == 'q') ? msgget(key, 0) :
201 (c == 'm') ? shmget(key, 0, 0) :
202 semget(key, 0, 0));
203
204 if (id < 0) {
205 char *errmsg;
206 error++;
207 switch(errno) {
208 case EACCES:
209 errmsg = "permission denied for key";
210 break;
211 case EIDRM:
212 errmsg = "already removed key";
213 break;
214 case ENOENT:
215 errmsg = "invalid key";
216 break;
217 default:
218 errmsg = "unknown error in key";
219 break;
220 }
221 bb_fprintf(stderr, "%s: %s (%s)\n",
222 prog, errmsg, optarg);
223 continue;
224 }
225 } else {
226 /* ids are in decimal */
227 id = strtoul(optarg, NULL, 10);
228 }
229
230 result = ((c == 'q') ? msgctl(id, IPC_RMID, NULL) :
231 (c == 'm') ? shmctl(id, IPC_RMID, NULL) :
232 semctl(id, 0, IPC_RMID, arg));
233
234 if (result < 0) {
235 char *errmsg;
236 error++;
237 switch(errno) {
238 case EACCES:
239 case EPERM:
240 errmsg = iskey
241 ? "permission denied for key"
242 : "permission denied for id";
243 break;
244 case EINVAL:
245 errmsg = iskey
246 ? "invalid key"
247 : "invalid id";
248 break;
249 case EIDRM:
250 errmsg = iskey
251 ? "already removed key"
252 : "already removed id";
253 break;
254 default:
255 errmsg = iskey
256 ? "unknown error in key"
257 : "unknown error in id";
258 break;
259 }
260 bb_fprintf(stderr, "%s: %s (%s)\n",
261 prog, errmsg, optarg);
262 continue;
263 }
264 }
265
266 /* print usage if we still have some arguments left over */
267 if (optind != argc) {
268 bb_show_usage();
269 }
270
271 /* exit value reflects the number of errors encountered */
272 return error;
273}
diff --git a/util-linux/ipcs.c b/util-linux/ipcs.c
new file mode 100644
index 000000000..23759eeda
--- /dev/null
+++ b/util-linux/ipcs.c
@@ -0,0 +1,686 @@
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
78struct 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 */
106union 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
131void do_shm (char format);
132void do_sem (char format);
133void do_msg (char format);
134void print_shm (int id);
135void print_msg (int id);
136void print_sem (int id);
137
138int 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
220static void
221print_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
247void 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
372void 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
474void 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
584void 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
612void 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
644void 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}