/*---------------------------------------------------------------------------------
Name               : proc_env.c
Author             : Marvin Raaijmakers
Description        : Contains code for copying a process environment.
Date of last change: 19-Jul-2008

    Copyright (C) 2008 Marvin Raaijmakers

    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 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.

    You can contact me at: marvinr(at)users(dot)sf(dot)net
    (replace (at) by @ and (dot) by .)
-----------------------------------------------------------------------------------*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <ctype.h>
#include <string.h>

#include <keytouchd.h>

#define MAX_ENVIRON_FILENAME_SIZE 256
#define MAX_STAT_FILENAME_SIZE 256
#define MAX_PROCESS_NAME_SIZE 256
#define MAX_ENV_NAME 256
#define MAX_ENV_VALUE 512

static void get_environment (char *proc_string);
static char str_only_digits (char *str);

void
get_environment (char *proc_string)
/*
Input:
  proc_string - The PID -in string representation- of the process to copy the
                environment from.
Description:
  This functions reads all environment variables (and their values) of the process
  with PID 'proc_string' from the file formed by 'proc_string' followed by
  "/environ". The current process will use all these values for its own envorinment
  variables.
*/
{
	char env_file_name[MAX_ENVIRON_FILENAME_SIZE];
	FILE *file;
	int i;
	char name[MAX_ENV_NAME],
	     value[MAX_ENV_VALUE],
	     c;
	
	snprintf (env_file_name, MAX_ENVIRON_FILENAME_SIZE, "%s/environ", proc_string);
	file = fopen (env_file_name, "rb");
	if (file)
	{
		while (!feof( file ))
		{
			c = '\0';
			i = 0;
			/* Read the name of the environment variable */
			while (c != '=' && !feof(file))
			{
				c = fgetc (file);
				if (c != '=' && i < MAX_ENV_NAME && !feof(file))
				{
					name[i] = c;
					i++;
				}
			}
			name[i] = '\0';
			
			i = 0;
			/* Read the value of the environment variable */
			while (c != '\0' && !feof(file))
			{
				c = fgetc (file);
				if (c != '\0' && i < MAX_ENV_VALUE && !feof(file))
				{
					value[i] = c;
					i++;
				}
			}
			value[i] = '\0';
			
			if (name[0])
			{
				setenv (name, value, 1);
				/*printf ("$%s = %s\n", name, value);*/
			}
		}
		fclose (file);
	}
}


char
str_only_digits (char *str)
{
	while (*str && isdigit(*str))
	{
		str++;
	}
	return *str == '\0';
}


Boolean
set_desktop_env_vars (char *desktop_proc_name)
/*
Input:
  desktop_proc_name - The name of the process to copy the envorinment from
Description:
  This function will search for a process that is named 'desktop_proc_name'. If such
  process exists then, all the values of the environment variables of the process
  will be used as the values for the env vars of the process that calls this
  function. The proc file system will be used for finding the process and reading
  its environment variables.
*/
{
	DIR *dp;
	struct dirent *ep;
	struct stat dir_stat;
	FILE *stat_file;
	int  uid,
	     i;
	char *working_dir,
	     stat_file_name[MAX_STAT_FILENAME_SIZE],
	     process_name[MAX_PROCESS_NAME_SIZE],
	     c;
	Boolean desktop_proc_found;
	
	uid = getuid();
	working_dir = get_current_dir_name();
	if (working_dir == NULL)
	{
		/* There is not enough memory */
		return FALSE;
	}
	
	desktop_proc_found = FALSE;
	
	chdir ("/proc");
	dp = opendir (".");
	if (dp != NULL)
	{
		/* We are going the check every process that has a directory in /proc/
		 * and is being executed by the current user.
		 */
		while ((ep = readdir(dp)) && !desktop_proc_found)
		{
			if (str_only_digits (ep->d_name))
			{
				stat (ep->d_name, &dir_stat);
				if (dir_stat.st_uid == uid) /* Does the user own this directory? */
				{
					snprintf (stat_file_name, MAX_STAT_FILENAME_SIZE, "%s/stat", ep->d_name);
					stat_file = fopen (stat_file_name, "r");
					if (stat_file)
					{
						/* Read from stat_file until we read a '(' or EOF */
						do {
							c = fgetc (stat_file);
						} while (c != EOF && c != '(');
						if (c != EOF)
						{
							/* Initialise process_name in case we don't read anything */
							process_name[0] = '\0';
							/* Read the process name from stat_file and put
							 * it in process_name */
							for (i = 0;
							     i < MAX_PROCESS_NAME_SIZE-1 &&
							      (c = fgetc (stat_file)) != ')' && c != EOF;
							     i++)
							{
								process_name[i] = c;
							}
							process_name[i] = '\0';
							
							/* We now have the name */
							if (strcmp (process_name, desktop_proc_name) == 0)
							{
								desktop_proc_found = TRUE;
								get_environment (ep->d_name);
							}
							
						}
						fclose (stat_file);
					}
				}
			}
		}
		(void) closedir (dp);
	}
	chdir (working_dir);
	free (working_dir);
	
	return desktop_proc_found;
}
