/*
 *   Copyright (C) Bull S.A. 1996
 *   Copyright (c) International Business Machines  Corp., 2001
 *
 *   This program is free software;  you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
 *   the GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program;  if not, write to the Free Software
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */
/*---------------------------------------------------------------------+
|                           semaphore_test_01                          |
| ==================================================================== |
|                                                                      |
| Description:  Simplistic test to verify the semget () and semctl ()  |
|               system function calls.                                 |
|                                                                      |
|               o  Create semaphore with semget ()                     |
|                                                                      |
|               o  Set the semaphore user id, group id and permission  |
|                  bits with semctl (IPC_SET)                          |
|                                                                      |
|               o  Retrieve the semaphore status with semctl (IPC_STAT)|
|                  and compare with expected values                    |
|                                                                      |
|               o  Print out the semaphore id for comparison with the  |
|                  output of the 'ipcs -s' command                     |
|                                                                      |
| System calls: The following system calls are made                    |
|                                                                      |
|               semget () - Gets a set of semaphores                   |
|               semctl () - Controls semaphore operations              |
|                                                                      |
| Usage:        semaphore_test_01                                      |
| NOTE: this program creates a system semaphore, but never deletes it. |
|       In order to delete it, use `ipcrm sem <id_num>`.  An alternate |
|       to this is to run the program as follows:                      |
|               ipcrm sem `./semaphore_test_01`                        |
|       This will delete the semaphore after the program exits, but    |
|       does not print out the semaphore id to see.                    |
|                                                                      |
| To compile:   cc -o semaphore_test_01 semaphore_test_01.c            |
|                                                                      |
| Last update:   Ver. 1.2, 2/14/94 00:18:16                           |
|                                                                      |
| Change Activity                                                      |
|                                                                      |
|   Version  Date    Name  Reason                                      |
|    0.1     050689  CTU   Initial version                             |
|    0.2     111993  DJK   Modify for AIX version 4.1                  |
|    1.2     021494  DJK   Move to "prod" directory                    |
|                                                                      |
+---------------------------------------------------------------------*/

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/sem.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>

#ifdef _LINUX_
#include <sys/stat.h>
#endif

#if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)
/* union semun is defined by including <sys/sem.h> */
#else
/* according to X/OPEN we have to define it ourselves */
union semun {
	int val;		/* value for SETVAL */
	struct semid_ds *buf;	/* buffer for IPC_STAT, IPC_SET */
	unsigned short *array;	/* array for GETALL, SETALL */
	/* Linux specific part: */
	struct seminfo *__buf;	/* buffer for IPC_INFO */
};
#endif

/* Defines
 *
 * NUM_SEMAPHORES: number of semaphores to create
 */
#define NUM_SEMAPHORES	1

/*
 * Function prototypes
 *
 * sys_error (): System error message function
 * error (): Error message function
 */
static void sys_error(const char *, int);
static void error(const char *, int);

/*---------------------------------------------------------------------+
|                               main                                   |
| ==================================================================== |
|                                                                      |
| Function:  Main program  (see prolog for more details)               |
|                                                                      |
| Returns:   (0)  Successful completion                                |
|            (-1) Error occurred                                       |
|                                                                      |
+---------------------------------------------------------------------*/
int main(int argc, char **argv)
{
	uid_t uid = getuid();	/* User's user id */
	gid_t gid = getgid();	/* User's group id */
	int semid;		/* Unique semaphore id */
	int nsems = NUM_SEMAPHORES;	/* Number of semaphores to create */
	struct semid_ds exp_semdata;	/* Expected semaphore values */
	union semun exp_semdatap;
	struct semid_ds act_semdata;	/* Actual semaphore values */
	union semun act_semdatap;

	exp_semdatap.buf = &exp_semdata;
	act_semdatap.buf = &act_semdata;

	umask(0000);

	/* SET semid_ds STRUCTURE TO DESIRED VALUES........ */

	/*
	 * Initialize the "expected" sempahore value structure
	 */
	exp_semdata.sem_perm.cuid = exp_semdata.sem_perm.uid = uid;
	exp_semdata.sem_perm.cgid = exp_semdata.sem_perm.gid = gid;
	exp_semdata.sem_perm.mode = 0660;
	exp_semdata.sem_nsems = nsems;

	/*
	 * Create a semaphore, set the semaphore fields and then
	 * retrieve the fields.
	 */
	if ((semid = semget(IPC_PRIVATE, nsems, IPC_CREAT | 0666)) < 0)
		sys_error("semget (IPC_PRIVATE) failed", __LINE__);

	if (semctl(semid, nsems, IPC_SET, exp_semdatap) < 0)
		sys_error("semctl (IPC_SET) failed", __LINE__);

	if (semctl(semid, nsems, IPC_STAT, act_semdatap) < 0)
		sys_error("semctl (IPC_STAT) failed", __LINE__);

	/*
	 * Verify that the semaphore fields were set correctly
	 */
	if (act_semdata.sem_perm.cuid != exp_semdata.sem_perm.cuid)
		error("sem_perm.cuid field was not set!", __LINE__);
	if (act_semdata.sem_perm.uid != exp_semdata.sem_perm.uid)
		error("sem_perm.uid field was not set!", __LINE__);
	if (act_semdata.sem_perm.cgid != exp_semdata.sem_perm.cgid)
		error("sem_perm.cgid field was not set!", __LINE__);
	if (act_semdata.sem_perm.gid != exp_semdata.sem_perm.gid)
		error("sem_perm.gid field was not set!", __LINE__);
	if (act_semdata.sem_perm.mode != exp_semdata.sem_perm.mode)
		error("sem_perm.mode field was not set!", __LINE__);
	if (act_semdata.sem_nsems != exp_semdata.sem_nsems)
		error("sem_nsems field was not set!", __LINE__);

	/*
	 * Print out the id of the newly created semaphore for comparison
	 * with the 'ipcs -s' command and then exit.
	 */
	printf("%d\n", semid);

	return (0);
}

/*---------------------------------------------------------------------+
|                             sys_error ()                             |
| ==================================================================== |
|                                                                      |
| Function:  Creates system error message and calls error ()           |
|                                                                      |
+---------------------------------------------------------------------*/
static void sys_error(const char *msg, int line)
{
	char syserr_msg[256];

	sprintf(syserr_msg, "%s: %s\n", msg, strerror(errno));
	error(syserr_msg, line);
}

/*---------------------------------------------------------------------+
|                               error ()                               |
| ==================================================================== |
|                                                                      |
| Function:  Prints out message and exits...                           |
|                                                                      |
+---------------------------------------------------------------------*/
static void error(const char *msg, int line)
{
	fprintf(stderr, "ERROR [line: %d] %s\n", line, msg);
	exit(-1);
}
