Home | History | Annotate | Line # | Download | only in kernel
      1  1.2    martin /*	$NetBSD: t_semtimedop.c,v 1.2 2024/10/10 07:45:02 martin Exp $	*/
      2  1.1  christos 
      3  1.1  christos /*-
      4  1.1  christos  * Copyright (c) 2024 The NetBSD Foundation, Inc.
      5  1.1  christos  * All rights reserved.
      6  1.1  christos  *
      7  1.1  christos  * Redistribution and use in source and binary forms, with or without
      8  1.1  christos  * modification, are permitted provided that the following conditions
      9  1.1  christos  * are met:
     10  1.1  christos  * 1. Redistributions of source code must retain the above copyright
     11  1.1  christos  *    notice, this list of conditions and the following disclaimer.
     12  1.1  christos  * 2. Redistributions in binary form must reproduce the above copyright
     13  1.1  christos  *    notice, this list of conditions and the following disclaimer in the
     14  1.1  christos  *    documentation and/or other materials provided with the distribution.
     15  1.1  christos  *
     16  1.1  christos  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     17  1.1  christos  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     18  1.1  christos  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     19  1.1  christos  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     20  1.1  christos  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     21  1.1  christos  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     22  1.1  christos  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     23  1.1  christos  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     24  1.1  christos  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     25  1.1  christos  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     26  1.1  christos  * POSSIBILITY OF SUCH DAMAGE.
     27  1.1  christos  */
     28  1.1  christos #include <sys/cdefs.h>
     29  1.2    martin __RCSID("$NetBSD: t_semtimedop.c,v 1.2 2024/10/10 07:45:02 martin Exp $");
     30  1.1  christos 
     31  1.1  christos #include <sys/types.h>
     32  1.1  christos #include <sys/ipc.h>
     33  1.1  christos #include <sys/sem.h>
     34  1.1  christos #include <errno.h>
     35  1.1  christos #include <string.h>
     36  1.1  christos #include <time.h>
     37  1.1  christos #include <stdlib.h>
     38  1.1  christos #include <stdio.h>
     39  1.1  christos #include <unistd.h>
     40  1.1  christos #include <atf-c.h>
     41  1.1  christos #include <sys/wait.h>
     42  1.1  christos 
     43  1.2    martin union semun {
     44  1.2    martin 	int	val;		/* value for SETVAL */
     45  1.2    martin 	struct	semid_ds *buf;	/* buffer for IPC_{STAT,SET} */
     46  1.2    martin 	u_short	*array;		/* array for GETALL & SETALL */
     47  1.2    martin };
     48  1.2    martin 
     49  1.1  christos ATF_TC(semtimedop_basic);
     50  1.1  christos ATF_TC_HEAD(semtimedop_basic, tc)
     51  1.1  christos {
     52  1.1  christos 	atf_tc_set_md_var(tc, "descr", "Basic semtimedop functionality");
     53  1.1  christos }
     54  1.1  christos 
     55  1.1  christos ATF_TC_BODY(semtimedop_basic, tc)
     56  1.1  christos {
     57  1.1  christos 	key_t		key = IPC_PRIVATE;
     58  1.1  christos 	int		semid;
     59  1.1  christos 	struct sembuf	sops;
     60  1.2    martin 	union semun	sun;
     61  1.1  christos 	struct timespec	timeout;
     62  1.1  christos 
     63  1.1  christos 	/* Create semaphore set with 1 semaphore */
     64  1.1  christos 	semid = semget(key, 1, IPC_CREAT | 0600);
     65  1.1  christos 	ATF_REQUIRE_MSG(semid != -1, "semget failed: %s", strerror(errno));
     66  1.1  christos 
     67  1.1  christos 	/* Initialize semaphore to 0 */
     68  1.2    martin 	sun.val = 0;
     69  1.2    martin 	if (semctl(semid, 0, SETVAL, sun) == -1) {
     70  1.1  christos 		ATF_REQUIRE_MSG(0, "semctl SETVAL failed: %s",
     71  1.1  christos 		    strerror(errno));
     72  1.1  christos 	}
     73  1.1  christos 
     74  1.1  christos 	/* Define semtimedop operation: increment semaphore */
     75  1.1  christos 	sops.sem_num = 0;
     76  1.1  christos 	sops.sem_op = 1;
     77  1.1  christos 	sops.sem_flg = 0;
     78  1.1  christos 
     79  1.1  christos 	/* Define timeout */
     80  1.1  christos 	timeout.tv_sec = 1;
     81  1.1  christos 	timeout.tv_nsec = 0;
     82  1.1  christos 
     83  1.1  christos 	/* Perform semtimedop */
     84  1.1  christos 	if (semtimedop(semid, &sops, 1, &timeout) == -1) {
     85  1.1  christos 		ATF_REQUIRE_MSG(0, "semtimedop failed: %s", strerror(errno));
     86  1.1  christos 	}
     87  1.1  christos 
     88  1.1  christos 	/* Check semaphore value */
     89  1.1  christos 	int val = semctl(semid, 0, GETVAL);
     90  1.1  christos 	ATF_REQUIRE_MSG(val == 1,
     91  1.1  christos 	    "Semaphore value incorrect: got %d, expected 1", val);
     92  1.1  christos 
     93  1.1  christos 	/* Clean up */
     94  1.1  christos 	ATF_REQUIRE_MSG(semctl(semid, 0, IPC_RMID) != -1,
     95  1.1  christos 	    "semctl IPC_RMID failed: %s", strerror(errno));
     96  1.1  christos }
     97  1.1  christos 
     98  1.1  christos /* semtimedop blocks until timeout expires */
     99  1.1  christos ATF_TC(semtimedop_timeout);
    100  1.1  christos ATF_TC_HEAD(semtimedop_timeout, tc)
    101  1.1  christos {
    102  1.1  christos 	atf_tc_set_md_var(tc, "descr",
    103  1.1  christos 	    "semtimedop blocks until timeout expires");
    104  1.1  christos }
    105  1.1  christos 
    106  1.1  christos ATF_TC_BODY(semtimedop_timeout, tc)
    107  1.1  christos {
    108  1.1  christos 	key_t		key = IPC_PRIVATE;
    109  1.1  christos 	int		semid;
    110  1.1  christos 	struct sembuf	sops;
    111  1.2    martin 	union semun	sun;
    112  1.1  christos 	struct timespec	timeout;
    113  1.1  christos 	pid_t		pid;
    114  1.1  christos 	int		status;
    115  1.1  christos 
    116  1.1  christos 	/* Create semaphore set with 1 semaphore */
    117  1.1  christos 	semid = semget(key, 1, IPC_CREAT | 0600);
    118  1.1  christos 	ATF_REQUIRE_MSG(semid != -1, "semget failed: %s", strerror(errno));
    119  1.1  christos 
    120  1.1  christos 	/* Initialize semaphore to 0 */
    121  1.2    martin 	sun.val = 0;
    122  1.2    martin 	if (semctl(semid, 0, SETVAL, sun) == -1) {
    123  1.1  christos 		ATF_REQUIRE_MSG(0, "semctl SETVAL failed: %s", strerror(errno));
    124  1.1  christos 	}
    125  1.1  christos 
    126  1.1  christos 	pid = fork();
    127  1.1  christos 	ATF_REQUIRE_MSG(pid != -1, "fork failed: %s", strerror(errno));
    128  1.1  christos 
    129  1.1  christos 	if (pid == 0) {
    130  1.1  christos 		/*
    131  1.1  christos 		 * Child: perform semtimedop with negative sem_op, should
    132  1.1  christos 		 * block until timeout
    133  1.1  christos 		 */
    134  1.1  christos 		sops.sem_num = 0;
    135  1.1  christos 		sops.sem_op = -1;
    136  1.1  christos 		sops.sem_flg = 0;
    137  1.1  christos 
    138  1.1  christos 		timeout.tv_sec = 2;
    139  1.1  christos 		timeout.tv_nsec = 0;
    140  1.1  christos 
    141  1.1  christos 		if (semtimedop(semid, &sops, 1, &timeout) == -1) {
    142  1.1  christos 			if (errno == EAGAIN || errno == EINTR) {
    143  1.1  christos 				exit(0);	/* Expected */
    144  1.1  christos 			}
    145  1.1  christos 		}
    146  1.1  christos 		exit(1);	/* Unexpected failure/success */
    147  1.1  christos 	}
    148  1.1  christos 
    149  1.1  christos 	/* Parent: wait for child to finish */
    150  1.1  christos 	waitpid(pid, &status, 0);
    151  1.1  christos 	ATF_REQUIRE(WIFEXITED(status));
    152  1.1  christos 	ATF_REQUIRE_EQ(WEXITSTATUS(status), 0);
    153  1.1  christos 
    154  1.1  christos 	/* Clean up */
    155  1.1  christos 	ATF_REQUIRE_MSG(semctl(semid, 0, IPC_RMID) != -1,
    156  1.1  christos 	    "semctl IPC_RMID failed: %s", strerror(errno));
    157  1.1  christos }
    158  1.1  christos 
    159  1.1  christos /* semtimedop with SEM_UNDO adjusts semaphore on exit */
    160  1.1  christos ATF_TC(semtimedop_semundo);
    161  1.1  christos ATF_TC_HEAD(semtimedop_semundo, tc)
    162  1.1  christos {
    163  1.1  christos 	atf_tc_set_md_var(tc, "descr",
    164  1.1  christos 	    "semtimedop with SEM_UNDO adjusts semaphore on exit");
    165  1.1  christos }
    166  1.1  christos 
    167  1.1  christos ATF_TC_BODY(semtimedop_semundo, tc)
    168  1.1  christos {
    169  1.1  christos 	key_t		key = IPC_PRIVATE;
    170  1.1  christos 	int		semid;
    171  1.1  christos 	struct sembuf	sops;
    172  1.2    martin 	union semun	sun;
    173  1.1  christos 	struct timespec	timeout;
    174  1.1  christos 	pid_t		pid;
    175  1.1  christos 	int		val;
    176  1.1  christos 
    177  1.1  christos 	/* Create semaphore set with 1 semaphore */
    178  1.1  christos 	semid = semget(key, 1, IPC_CREAT | 0600);
    179  1.1  christos 	ATF_REQUIRE_MSG(semid != -1, "semget failed: %s", strerror(errno));
    180  1.1  christos 
    181  1.1  christos 	/* Initialize semaphore to 0 */
    182  1.2    martin 	sun.val = 0;
    183  1.2    martin 	if (semctl(semid, 0, SETVAL, sun) == -1) {
    184  1.1  christos 		ATF_REQUIRE_MSG(0, "semctl SETVAL failed: %s", strerror(errno));
    185  1.1  christos 	}
    186  1.1  christos 
    187  1.1  christos 	pid = fork();
    188  1.1  christos 	ATF_REQUIRE_MSG(pid != -1, "fork failed: %s", strerror(errno));
    189  1.1  christos 
    190  1.1  christos 	if (pid == 0) {
    191  1.1  christos 		/* Child: perform semtimedop with SEM_UNDO */
    192  1.1  christos 		sops.sem_num = 0;
    193  1.1  christos 		sops.sem_op = 1;
    194  1.1  christos 		sops.sem_flg = SEM_UNDO;
    195  1.1  christos 
    196  1.1  christos 		timeout.tv_sec = 1;
    197  1.1  christos 		timeout.tv_nsec = 0;
    198  1.1  christos 
    199  1.1  christos 		if (semtimedop(semid, &sops, 1, &timeout) == -1) {
    200  1.1  christos 			exit(1);	/* Unexpected failure */
    201  1.1  christos 		}
    202  1.1  christos 
    203  1.1  christos 		exit(0); /* Exit normally, SEM_UNDO should be triggered */
    204  1.1  christos 	}
    205  1.1  christos 
    206  1.1  christos 	/* Parent: wait for child to exit */
    207  1.1  christos 	waitpid(pid, NULL, 0);
    208  1.1  christos 
    209  1.1  christos 	/* Check semaphore value; should be 0 after SEM_UNDO */
    210  1.1  christos 	val = semctl(semid, 0, GETVAL);
    211  1.1  christos 	ATF_REQUIRE_MSG(val == 0,
    212  1.1  christos 	    "Semaphore value incorrect after SEM_UNDO: got %d, "
    213  1.1  christos 	    "expected 0", val);
    214  1.1  christos 
    215  1.1  christos 	/* Clean up */
    216  1.1  christos 	ATF_REQUIRE_MSG(semctl(semid, 0, IPC_RMID) != -1,
    217  1.1  christos 	    "semctl IPC_RMID failed: %s", strerror(errno));
    218  1.1  christos }
    219  1.1  christos 
    220  1.1  christos /* semtimedop handles invalid parameters correctly */
    221  1.1  christos ATF_TC(semtimedop_invalid);
    222  1.1  christos ATF_TC_HEAD(semtimedop_invalid, tc)
    223  1.1  christos {
    224  1.1  christos 	atf_tc_set_md_var(tc, "descr",
    225  1.1  christos 	    "semtimedop handles invalid parameters correctly");
    226  1.1  christos }
    227  1.1  christos 
    228  1.1  christos ATF_TC_BODY(semtimedop_invalid, tc)
    229  1.1  christos {
    230  1.1  christos 	struct sembuf	sops;
    231  1.2    martin 	union semun	sun;
    232  1.1  christos 	struct timespec	timeout;
    233  1.1  christos 
    234  1.1  christos 	/* Invalid semaphore id */
    235  1.1  christos 	sops.sem_num = 0;
    236  1.1  christos 	sops.sem_op = -1;
    237  1.1  christos 	sops.sem_flg = 0;
    238  1.1  christos 
    239  1.1  christos 	timeout.tv_sec = 1;
    240  1.1  christos 	timeout.tv_nsec = 0;
    241  1.1  christos 
    242  1.1  christos 	/* Attempt to perform semtimedop on invalid semid */
    243  1.1  christos 	ATF_REQUIRE_MSG(semtimedop(-1, &sops, 1, &timeout) == -1
    244  1.1  christos 	    && errno == EINVAL, "semtimedop did not fail on invalid semid");
    245  1.1  christos 
    246  1.1  christos 	/* Create semaphore set */
    247  1.1  christos 	key_t	key = IPC_PRIVATE;
    248  1.1  christos 	int	semid = semget(key, 1, IPC_CREAT | 0600);
    249  1.1  christos 	ATF_REQUIRE_MSG(semid != -1, "semget failed: %s", strerror(errno));
    250  1.1  christos 
    251  1.1  christos 	/* Initialize semaphore to 0 */
    252  1.2    martin 	sun.val = 0;
    253  1.2    martin 	if (semctl(semid, 0, SETVAL, sun) == -1) {
    254  1.1  christos 		ATF_REQUIRE_MSG(0, "semctl SETVAL failed: %s", strerror(errno));
    255  1.1  christos 	}
    256  1.1  christos 
    257  1.1  christos 	/* Set an invalid semaphore number */
    258  1.1  christos 	sops.sem_num = 1;	/* Only 1 semaphore in set, index 0 */
    259  1.1  christos 	sops.sem_op = 1;
    260  1.1  christos 	sops.sem_flg = 0;
    261  1.1  christos 
    262  1.1  christos 	/* Attempt semtimedop with invalid sem_num */
    263  1.1  christos 	ATF_REQUIRE_MSG(semtimedop(semid, &sops, 1, &timeout) == -1
    264  1.1  christos 	    && errno == EFBIG, "semtimedop did not fail on invalid sem_num");
    265  1.1  christos 
    266  1.1  christos 	/* Clean up */
    267  1.1  christos 	ATF_REQUIRE_MSG(semctl(semid, 0, IPC_RMID) != -1,
    268  1.1  christos 	    "semctl IPC_RMID failed: %s", strerror(errno));
    269  1.1  christos }
    270  1.1  christos 
    271  1.1  christos ATF_TP_ADD_TCS(tp)
    272  1.1  christos {
    273  1.1  christos 	ATF_TP_ADD_TC(tp, semtimedop_basic);
    274  1.1  christos 	ATF_TP_ADD_TC(tp, semtimedop_timeout);
    275  1.1  christos 	ATF_TP_ADD_TC(tp, semtimedop_semundo);
    276  1.1  christos 	ATF_TP_ADD_TC(tp, semtimedop_invalid);
    277  1.1  christos 
    278  1.1  christos 	return atf_no_error();
    279  1.1  christos }
    280