// Ejemplo De Semáforos Con N Procesos Y 1 Semaforo
// Varios Procesos Compiten Para Escribir En Un Fichero
// Un Semáforo Pondrá Orden En La Competencia
// Gilberto Stankiewicz
// http://www.stan.com.mx
// ESCOM, 4CM4
// Febrero 2008
// GCC

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h> // ficheros
#include <sys/ipc.h> // semáforos
#include <sys/sem.h> // semáforos

void cerrarSemaforo (int semid, int pid) {
	struct sembuf operacion;
	operacion.sem_num = pid;
	operacion.sem_op = -1;
	operacion.sem_flg = 0;
	semop(semid, &operacion, 1);
}

void abrirSemaforo (int semid, int pid) {
	struct sembuf operacion;
	operacion.sem_num = pid;
	operacion.sem_op = 1;
	operacion.sem_flg = 0;
	semop(semid, &operacion, 1);
}

int abrirArchivo (char *path) {
	int fd;
	if ((fd = open(path, O_WRONLY | O_CREAT | O_APPEND, 0644)) == -1) {
		perror("open");
		exit(-1);
	}
	return fd;
}

void cerrarArchivo (int fd) {
	if (close(fd) == -1) {
		perror("close");
		exit(-1);
	}
}

void escribirArchivo (int fd, char *str) {
	int nbytes;
	if ((nbytes = write(fd, str, strlen(str))) == -1) {
		perror("write");
		exit(-1);
	}	
}

void salida(char *path, char *str) {
	int fd;
	fd = abrirArchivo(path);
	escribirArchivo(fd, str);
	cerrarArchivo(fd);
}

int main (int argc, char *argv[]) {
	int procesos;
	int semid, pid, llave, i;
	char str[50];
	
	// parámetro de entrada: procesos
	procesos = 10;
	if (argc >= 2)
		procesos = atoi(argv[1]);
	
	// crear llave
	if ((llave = ftok(argv[0],'M')) == -1) {
		fprintf(stderr, "Error al crear la llave. \n", argv[0]);
		exit(-1);
	}
	
	// crear identificador con un semáforo
	if ((semid = semget(llave, 1, IPC_CREAT | 0600)) == -1) {
		fprintf(stderr, "Error al crear el identificador. \n", argv[0]);
		exit(-1);
	}
	
	// abrir semáforo
	semctl(semid, 0, SETVAL, 1);
	
	// crear procesos
	for (i = 0; i < procesos; i++) {
		if ((pid = fork()) == -1) {
			perror("fork");
			exit(-1);
		}
		else if (pid == 0) {
			sprintf(str, "proceso hijo  %d \n", getpid());
			break;
		}
		else {
			sprintf(str, "proceso padre %d \n", getpid());
		}
	}

	// escribir en el archivo
	cerrarSemaforo(semid, 0);
	salida("EjemploSemaforosSalida.txt", str);
	abrirSemaforo(semid, 0);
	
	// borrar semáforo
	semctl(semid, 0, IPC_RMID, 0);
	
	// return 0; implícito en C99
}
