/////////////////////////////////////////////////////////////////////////////
// Name:        eviacamloader.c
// Purpose:  
// Author:      Cesar Mauri Loba (cesar at crea-si dot com)
// Modified by: 
// Created:     09/06/2010 
// Copyright:   (C) 2010 Cesar Mauri Loba - CREA Software Systems
// 
//  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 3 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, see <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>
#include <sys/stat.h>

/* check wait status to see if child process finished abnormaly */
int wait_status_correct (int status)
{
	/* Doesn't take into account an abnormal exit status. Usually it means that eviacam has 
	   finished due to a fatal error (e.g. camera not found or cancelled by the user) */
	/*	
	if (WEXITSTATUS(status)!= 0) {
		fprintf (stderr, "process finished abnormally. exit status: %d\n", WEXITSTATUS(status));
		return 0;
	}*/
	if (WIFSIGNALED(status)) {
		if (WCOREDUMP(status)) {
			fprintf (stderr, "process finished abnormally. core dumped\n");
			return 0;
		}

		int signum= WTERMSIG(status);
		if (signum== SIGSEGV) {
			fprintf (stderr, "process finished abnormally. segmentation fault\n");
			return 0;
		}
		if (signum== SIGBUS) {
			fprintf (stderr, "process finished abnormally. bus error\n");
			return 0;
		}
	}
	return 1;
}

int main (int argc, char **argv)
{

#ifndef NDEBUG
	printf ("Use: %d, Group: %d. Eff. usr: %d, Eff. group: %d\n", getuid(), getgid(), geteuid(), getegid());
#endif	

	if (geteuid()== 0) {
		/* running as root, change priority */
		if (nice(-11)== -1) {
			perror("nice");
			return -1;
		}

		/* privileges revocation */
		if (setgid (getgid())== -1 || setuid (getuid())== -1) {
			perror("setgid/setuid");
			return -1;
		}
	}
	else
		fprintf (stderr, "%s: this program needs root privileges. not renicing.\n", argv[0]);

	/* run eviacam until normal exit */
	for (;;) {
		/* create child process to run eviacam */
		pid_t pid= fork();
		if (pid== -1) {
			perror ("fork");
			return -1;
		}
		else if (pid== 0) {
			/* child process */
			
			/* 
				workaround to enable sound on systems with pulseaudio
				see [1] for more details
				[1] https://sourceforge.net/tracker/?func=detail&aid=3095561&group_id=248049&atid=1199422
				TODO: remove when this gets fixed
			*/
				
			/* check is padsp command can be run */
			int padsp_command_available= 0;	
			struct stat stat_buf;
			if (!stat("/usr/bin/padsp", &stat_buf))	padsp_command_available++;
			
			if (padsp_command_available) {
				if (execl ("/usr/bin/padsp", "eviacam", "@prefix@/bin/eviacam", NULL)== -1) {
					perror("execl");
					return -1;
				}
			}
			else {
				if (execl ("@prefix@/bin/eviacam", "eviacam", NULL)== -1) {
					perror("execl");
					return -1;
				}
			}
		}
		else
		{
			/* parent process */
			int status;

			if (wait(&status)== -1) {
				perror ("wait");
				return -1;
			}

			/* check whether the child process finished abnormaly */
			if (!wait_status_correct(status)) {
				fprintf (stderr, "%s: abnormal process termination, restarting\n", argv[0]);
				sleep (1);
			}
			else	break;
		}

	}

#ifndef NDEBUG
	printf ("Use: %d, Group: %d. Eff. usr: %d, Eff. group: %d\n", getuid(), getgid(), geteuid(), getegid());
#endif	
	return 0;
}

