1 /* Copyright (c) 2003 Michael B. Allen <mba2000 ioplex.com>
2 *
3 * The MIT License
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included
13 * in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
19 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21 * OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <stdarg.h>
28 #include <errno.h>
29 #include <limits.h>
30
31 #include <fcntl.h>
32 #include <unistd.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <sys/ipc.h>
36 #include <sys/sem.h>
37 #include <sys/mman.h>
38 #include <sys/wait.h>
39
40 #define FLAGS_SEMUNDO 1
41 #define IS_SEMUNDO(cmd) (((cmd)->flags & FLAGS_SEMUNDO) != 0)
42
43 #define MAX_TRIES 3
44 #define MAX_SEMNUM 255
45
46 #define CMD_OPEN 1
47 #define CMD_REMOVE 2
48 #define CMD_POST 3
49 #define CMD_POSTMULT 4
50 #define CMD_WAIT 5
51 #define CMD_TRYWAIT 6
52 #define CMD_SETVAL 7
53 #define CMD_GETVAL 8
54
55 #define ERR(e,s) fprintf(stderr, "%d: %s: %s\n", __LINE__, (s), strerror(e))
56
57 static const char *cmdnames[] = {
58 NULL, "CMD_OPEN", "CMD_REMOVE", "CMD_POST", "CMD_POSTMULT",
59 "CMD_WAIT", "CMD_TRYWAIT", "CMD_SETVAL", "CMD_GETVAL"
60 };
61
62 static const char *cmdflags = " orpmwtsg";
63
64 struct cmd {
65 int cmd;
66 int semid;
67 int semnum;
68 const char *path;
69 int flags;
70 int oflags;
71 mode_t mode;
72 int numsems;
73 int value;
74 };
75
76 void
cmd_print(struct cmd * cmd)77 cmd_print(struct cmd *cmd)
78 {
79 char oflags[255], *o = oflags;
80 *o = '\0';
81 if ((cmd->oflags & O_CREAT))
82 o += sprintf(o, "O_CREAT");
83 if ((cmd->oflags & O_EXCL))
84 o += sprintf(o, "|O_EXCL");
85 fprintf(stderr,
86 "%s: semid: %d semnum: %d path: %s flags: %s oflags: %s mode: %04o numsems: %d value: %d\n",
87 cmdnames[cmd->cmd], cmd->semid, cmd->semnum, cmd->path ? cmd->path : "",
88 IS_SEMUNDO(cmd) ? "FLAGS_SEMUNDO" : "", oflags, cmd->mode, cmd->numsems, cmd->value);
89 }
90
91
92 #if defined(_SEM_SEMUN_UNDEFINED) || \
93 (defined(__digital__) && defined(__unix__)) || \
94 defined(_HPUX_SOURCE)
95 union semun {
96 int val; /* value for SETVAL */
97 struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */
98 unsigned short *array; /* array for GETALL, SETALL */
99 /* Linux specific part: */
100 struct seminfo *__buf; /* buffer for IPC_INFO */
101 };
102 #endif
103
104 /* Open or create a semaphore initializing it as necessary.
105 */
106 static int
semid_get(const char * name,int nsems,int oflags,mode_t mode,int value)107 semid_get(const char *name, int nsems, int oflags, mode_t mode, int value)
108 {
109 key_t key;
110 int max;
111
112 if (nsems > MAX_SEMNUM) {
113 ERR(errno = ERANGE, "semid_get");
114 return -1;
115 }
116 if ((key = ftok((char *)name, 1)) == (key_t)-1) {
117 ERR(errno, "ftok");
118 return -1;
119 }
120
121 /* This following loop ensures that we know if the semaphore was created
122 * as opposed to just opened so that it can be initialized properly. We
123 * do this by alternating between oflags 0 and IPC_CREATE|IPC_EXCL until
124 * one succeeds.
125 */
126 for (max = MAX_TRIES; max; max--) {
127 int semid;
128 union semun arg;
129
130 if ((oflags & O_EXCL) == 0) {
131 if ((semid = semget(key, nsems, 0)) != -1) {
132 struct semid_ds buf;
133
134 /* This inner try-loop ensures that the semaphore is initialized before
135 * we return even if the semaphore has been created with semget but not
136 * yet initialized with semctl. See Stevens' UNPv2 p274.
137 */
138 arg.buf = &buf;
139 for (max = MAX_TRIES; max; max--) {
140 if (semctl(semid, 0, IPC_STAT, arg) == -1) {
141 ERR(errno, "semctl");
142 return -1;
143 }
144 if (buf.sem_otime != 0) {
145 return semid;
146 }
147 sleep(1);
148 }
149
150 ERR(errno = ETIMEDOUT, "semid_get");
151 return -1;
152 } else if (errno != ENOENT) {
153 ERR(errno, "semget");
154 return -1;
155 }
156 }
157 if ((semid = semget(key, nsems, IPC_CREAT | IPC_EXCL | (mode & 0777))) != -1) {
158 struct sembuf initop;
159
160 if (nsems > 1) {
161 unsigned short array[MAX_SEMNUM * sizeof(unsigned short)];
162 int i;
163
164 arg.array = array;
165 arg.array[0] = 0; /* leave the first one 0 to be set with semop */
166 for (i = 1; i < nsems; i++) {
167 arg.array[i] = value;
168 }
169 if (semctl(semid, 0, SETALL, arg) == -1) {
170 ERR(errno, "semctl");
171 semctl(semid, 0, IPC_RMID);
172 return -1;
173 }
174 } else {
175 arg.val = 0;
176 if (semctl(semid, 0, SETVAL, arg) == -1) {
177 ERR(errno, "semctl");
178 semctl(semid, 0, IPC_RMID);
179 return -1;
180 }
181 }
182 /* increment by value to set sem_otime nonzero */
183 initop.sem_num = 0;
184 initop.sem_op = value;
185 initop.sem_flg = 0;
186 if (semop(semid, &initop, 1) == -1) {
187 ERR(errno, "semop");
188 semctl(semid, 0, IPC_RMID);
189 return -1;
190 }
191
192 return semid;
193 } else if ((oflags & O_EXCL) || errno != EEXIST) {
194 ERR(errno, "semget");
195 return -1;
196 }
197 }
198
199 ERR(errno = ETIMEDOUT, "semid_get");
200 return -1;
201 }
202 static int
cmd_open(struct cmd * cmd)203 cmd_open(struct cmd *cmd)
204 {
205 int semid, fd = 0;
206 char oflags[255], *o = oflags;
207
208 if ((cmd->oflags & O_CREAT) && (fd = open(cmd->path, O_CREAT, cmd->mode)) == -1) {
209 ERR(errno, "open");
210 return -1;
211 }
212
213 if ((semid = semid_get(cmd->path, cmd->numsems, cmd->oflags, cmd->mode, cmd->value)) == -1) {
214 return -1;
215 }
216 *o = '\0';
217 if ((cmd->oflags & O_CREAT))
218 o += sprintf(o, "O_CREAT");
219 if ((cmd->oflags & O_EXCL))
220 o += sprintf(o, "|O_EXCL");
221 fprintf(stderr, "%d = cmd_open(\"%s\", %d, %s, %04o, %d)\n",
222 semid, cmd->path, cmd->numsems, oflags, cmd->mode, cmd->value);
223
224 if (fd) close(fd);
225
226 printf("%d", semid);
227
228 return 0;
229 }
230 static int
cmd_remove(struct cmd * cmd)231 cmd_remove(struct cmd *cmd)
232 {
233 fprintf(stderr, "cmd_remove(%d)\n", cmd->semid);
234 if (semctl(cmd->semid, 0, IPC_RMID) == -1) {
235 ERR(errno, "semctl");
236 return -1;
237 }
238
239 return 0;
240 }
241 static int
cmd_wait(struct cmd * cmd)242 cmd_wait(struct cmd *cmd)
243 {
244 struct sembuf wait;
245
246 wait.sem_num = cmd->semnum;
247 wait.sem_op = -1;
248 wait.sem_flg = IS_SEMUNDO(cmd) ? SEM_UNDO : 0;
249
250 fprintf(stderr, "cmd_wait(%d, %d, %s)\n",
251 cmd->semid, cmd->semnum, IS_SEMUNDO(cmd) ? "SEM_UNDO" : "0");
252 if (semop(cmd->semid, &wait, 1) == -1) {
253 ERR(errno, "semop");
254 return -1;
255 }
256
257 return 0;
258 }
259 static int
cmd_trywait(struct cmd * cmd)260 cmd_trywait(struct cmd *cmd)
261 {
262 struct sembuf wait;
263
264 wait.sem_num = cmd->semnum;
265 wait.sem_op = -1;
266 wait.sem_flg = IPC_NOWAIT | (IS_SEMUNDO(cmd) ? SEM_UNDO : 0);
267
268 fprintf(stderr, "cmd_wait(%d, %d, %s)\n",
269 cmd->semid, cmd->semnum, IS_SEMUNDO(cmd) ? "IPC_NOWAIT|SEM_UNDO" : "IPC_NOWAIT");
270 if (semop(cmd->semid, &wait, 1) == -1) {
271 ERR(errno, "semop");
272 return errno == EAGAIN ? 0 : -1;
273 }
274
275 return 0;
276 }
277 static int
cmd_post(struct cmd * cmd)278 cmd_post(struct cmd *cmd)
279 {
280 struct sembuf post;
281
282 post.sem_num = cmd->semnum;
283 post.sem_op = 1;
284 post.sem_flg = IS_SEMUNDO(cmd) ? SEM_UNDO : 0;
285
286 fprintf(stderr, "cmd_post(%d, %d, %s)\n",
287 cmd->semid, cmd->semnum, IS_SEMUNDO(cmd) ? "SEM_UNDO" : "0");
288 if (semop(cmd->semid, &post, 1) == -1) {
289 ERR(errno, "semop");
290 return -1;
291 }
292
293 return 0;
294 }
295 static int
cmd_post_multiple(struct cmd * cmd)296 cmd_post_multiple(struct cmd *cmd)
297 {
298 struct sembuf post;
299 int count = cmd->value;
300
301 post.sem_num = cmd->semnum;
302 post.sem_op = 1;
303 post.sem_flg = IS_SEMUNDO(cmd) ? SEM_UNDO : 0;
304
305 fprintf(stderr, "cmd_post_multiple(%d, %d, %s, %d)\n",
306 cmd->semid, cmd->semnum, IS_SEMUNDO(cmd) ? "SEM_UNDO" : "0", count);
307 while (count--) {
308 if (semop(cmd->semid, &post, 1) == -1) {
309 ERR(errno, "semop");
310 return -1;
311 }
312 }
313
314 return 0;
315 }
316 static int
cmd_getvalue(struct cmd * cmd)317 cmd_getvalue(struct cmd *cmd)
318 {
319 int v;
320
321 if ((v = semctl(cmd->semid, cmd->semnum, GETVAL, 0)) == -1) {
322 ERR(errno, "semctl");
323 return -1;
324 }
325 fprintf(stderr, "%d = cmd_getvalue(%d, %d, GETVAL)\n", v, cmd->semid, cmd->semnum);
326
327 return 0;
328 }
329 static int
cmd_setvalue(struct cmd * cmd)330 cmd_setvalue(struct cmd *cmd)
331 {
332 union semun arg;
333
334 arg.val = cmd->value;
335 if (semctl(cmd->semid, cmd->semnum, SETVAL, arg) == -1) {
336 ERR(errno,"semctl");
337 return -1;
338 }
339 fprintf(stderr, "cmd_setvalue(%d, %d, SETVAL)\n", cmd->semid, cmd->semnum);
340
341 return 0;
342 }
343
344 typedef int (*cmd_fn)(struct cmd *cmd);
345 cmd_fn cmd_functions[] = {
346 NULL,
347 cmd_open,
348 cmd_remove,
349 cmd_post,
350 cmd_post_multiple,
351 cmd_wait,
352 cmd_trywait,
353 cmd_setvalue,
354 cmd_getvalue
355 };
356
357 int
main(int argc,char * argv[])358 main(int argc, char *argv[])
359 {
360 struct cmd cmd;
361
362 if (argc < 4) {
363 usage:
364 fprintf(stderr, "usage: semc <path|semid and semnum> -o|r|p|m|w|t|g|s <cmdargs>\n"
365 " EXAMPLES:\n"
366 " open: semc /tmp/foo -o 1 1 777 250 99\n"
367 /* argv: 0 1 2 3 4 5 6 7 */
368 " remove: semc 5441 I -r\n"
369 " post: semc 5441 3 -p\n"
370 "post multiple: semc 5441 3 -m 10\n"
371 " wait: semc 5441 3 -w\n"
372 " try wait: semc 5441 3 -t\n"
373 " get: semc 5441 3 -g\n"
374 " set: semc 5441 3 -s 99\n");
375 fputs("The -o command takes a path and command arguments O_CREAT and/or "
376 "O_EXCL and/or 0, mode_t in octal, numsems, and the value with which "
377 "the semaphore(s) should be initialize. All other commands take a semid "
378 "and semnum but -r igores semnum.\n", stderr);
379 return EXIT_FAILURE;
380 }
381
382 memset(&cmd, 0, sizeof cmd);
383 /* Determine Command */
384 if (strcmp(argv[2], "-o") == 0) {
385 cmd.cmd = CMD_OPEN;
386 } else if (*argv[3] == '-') {
387 char *ch = strchr(cmdflags, argv[3][1]);
388 if (!ch) {
389 fprintf(stderr, "Invalid option: %s\n", argv[2]);
390 goto usage;
391 }
392 cmd.cmd = ch - cmdflags;
393 } else {
394 fprintf(stderr, "Invalid options\n");
395 goto usage;
396 }
397 /* Populate struct cmd with Arguments */
398 switch (cmd.cmd) {
399 case CMD_OPEN:
400 if (argc < 8) {
401 fprintf(stderr, "Too few arguments for command\n");
402 goto usage;
403 }
404 cmd.path = argv[1];
405 if (*argv[3] == '1') cmd.oflags |= O_CREAT;
406 if (*argv[4] == '1') cmd.oflags |= O_EXCL;
407 {
408 unsigned long mode;
409
410 if ((mode = strtoul(argv[5], NULL, 8)) == ULONG_MAX) {
411 ERR(errno, "strtoul");
412 goto usage;
413 }
414 cmd.mode = mode;
415 }
416 if ((unsigned long)(cmd.numsems = strtoul(argv[6], NULL, 0)) == ULONG_MAX) {
417 ERR(errno, "strtoul");
418 goto usage;
419 }
420 if ((unsigned long)(cmd.value = strtoul(argv[7], NULL, 0)) == ULONG_MAX) {
421 ERR(errno, "strtoul");
422 goto usage;
423 }
424 break;
425 case CMD_POSTMULT:
426 case CMD_SETVAL:
427 if ((unsigned long)(cmd.value = strtoul(argv[4], NULL, 0)) == ULONG_MAX) {
428 ERR(errno, "strtoul");
429 goto usage;
430 }
431 case CMD_REMOVE:
432 case CMD_POST:
433 case CMD_WAIT:
434 case CMD_TRYWAIT:
435 case CMD_GETVAL:
436 if ((unsigned long)(cmd.semid = strtoul(argv[1], NULL, 0)) == ULONG_MAX ||
437 (unsigned long)(cmd.semnum = strtoul(argv[2], NULL, 0)) == ULONG_MAX) {
438 ERR(errno, "strtoul");
439 goto usage;
440 }
441 }
442
443 cmd_print(&cmd);
444 /* Run the Command */
445 if (cmd_functions[cmd.cmd](&cmd) == -1) {
446 fprintf(stderr, "Command failed\n");
447 return EXIT_FAILURE;
448 }
449
450 return EXIT_SUCCESS;
451 }
452
453