/*
 * acpi_helper.cpp - acpi helper 
 *
 * Copyright (c) 2002 Paul Campbell <paul@taniwha.com>
 *
 *  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

//
//	README!!
//
//	This file contains code that is intended to be run setuid root
//	(only if the end user enables it themselves, it's not set that
//	way as part of a standard KDE build).
//
//	Because of this this code should be simple and easily visually
//	inspected for security holes and/or bugs - if you feel the need
//	to change this file please get someone else to review your work
//	(I'll happily do it for you - mail me at paul@taniwha.com, please
//	review mine!)
//
//	I recommend the following practices here - both for safety and
//	transparency:
//
//		- check all array references (snprintf/strncpy etc)
//
//		- avoid malloc/new calls and pointers  too if possible
//

#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <string.h>

#define MAX_TOSHIBA_STRING 64
	
int
main(int argc, char **argv)
{
	int fd;
	int i;
	int toshibalcd_val = 0;

	::close(0);	// we're setuid - this is just in case
	for (i = 1; i < argc; i++)
	if (strcmp(argv[i], "--suspend") == 0 || strcmp(argv[i], "-suspend") == 0) {
		sync();
		sync();
		fd = open("/proc/acpi/sleep", O_RDWR);
		if (fd < 0)
			exit(1);
		write(fd, "3", 1);
		close(fd);
        	setuid(getuid());	// drop all priority asap
		exit(0);
	} else
	if (strcmp(argv[i], "--standby") == 0 || strcmp(argv[i], "-standby") == 0) {
		sync();
		sync();
		fd = open("/proc/acpi/sleep", O_RDWR);
		if (fd < 0)
			exit(1);
		write(fd, "1", 1);
		close(fd);
        	setuid(getuid());	// drop all priority asap
		exit(0);
	} else
	if (strcmp(argv[i], "--standby2") == 0 || strcmp(argv[i], "-standby2") == 0) {
		sync();
		sync();
		fd = open("/proc/acpi/sleep", O_RDWR);
		if (fd < 0)
			exit(1);
		write(fd, "2", 1);
		close(fd);
        	setuid(getuid());	// drop all priority asap
		exit(0);
	} else
	if (strcmp(argv[i], "--hibernate") == 0 || strcmp(argv[i], "-hibernate") == 0) {
		sync();
		sync();
		fd = open("/proc/acpi/sleep", O_RDWR);
		if (fd < 0)
			exit(1);
		write(fd, "4", 1);
		close(fd);
        	setuid(getuid());	// drop all priority asap
		exit(0);
	} else
	if (strcmp(argv[i], "--software-suspend") == 0 || strcmp(argv[i], "-software-suspend") == 0) {
		struct stat sb;
		int err;
		if ((err = stat("/usr/sbin/hibernate", &sb)) != 0 || sb.st_mode&S_IWOTH) {
			if (err != 0) {
				fprintf(stderr, "Can't find /usr/sbin/hibernate\n");
			} else {
				fprintf(stderr, "/usr/sbin/hibernate is writeable by anyone - we don't trust it\n");
			}
			exit(0);
		}
		::setuid(::geteuid());					// otherwise bash will throw it away
		::execl("/bin/sh", "-c", "/usr/sbin/hibernate", 0);	// this is not KDE environment code 
		exit(0);
	} else
	if (strcmp(argv[i], "--throttling") == 0 || strcmp(argv[i], "-throttling") == 0) {
		int val;
		char tmp[256];

		i++;
		if (i >= argc) 
			break;
		if (strlen(argv[i]) > 50 || strchr(argv[i], '.'))
			break;
		snprintf(tmp, sizeof(tmp), "/proc/acpi/processor/%s/throttling", argv[i]);
		tmp[sizeof(tmp)-1] = 0;
		i++;
		if (i >= argc) 
			break;
		val= atoi(argv[i]);
		if (val < 0)
			break;
		sync();
		sync();
		fd = open(tmp, O_RDWR);
		if (fd < 0)
			exit(1);
		snprintf(tmp, sizeof(tmp), "%d", val);
		write(fd, tmp, strlen(tmp));
		close(fd);
        	setuid(getuid());	// drop all priority asap
		exit(0);
	} else
	if (strcmp(argv[i], "--performance") == 0 || strcmp(argv[i], "-performance") == 0) {
		int val;
		char tmp[256];

		i++;
		if (i >= argc) 
			break;
		if (strlen(argv[i]) > 50 || strchr(argv[i], '.'))
			break;
		snprintf(tmp, sizeof(tmp), "/proc/acpi/processor/%s/performance", argv[i]);
		tmp[sizeof(tmp)-1] = 0;
		i++;
		if (i >= argc) 
			break;
		val= atoi(argv[i]);
		if (val < 0)
			break;
		sync();
		sync();
		fd = open(tmp, O_RDWR);
		if (fd < 0)
			exit(1);
		snprintf(tmp, sizeof(tmp), "%d", val);
		write(fd, tmp, strlen(tmp));
		close(fd);
        	setuid(getuid());	// drop all priority asap
		exit(0);
	} else
	if (strcmp(argv[i], "--toshibalcd") == 0 || strcmp(argv[i], "-toshibalcd") == 0) {
		
		i++;
		if (i >= argc) 
			break;
		toshibalcd_val= atoi(argv[i]);
		if (toshibalcd_val < 0)
			toshibalcd_val = 0;
		if (toshibalcd_val > 7)
			toshibalcd_val = 7;
		fd = open("/proc/acpi/TOSHIBA1/lcd", O_RDWR);
		if (fd >= 0 ) {
		    char c;
		    
		    c = '0'+toshibalcd_val;
		    write(fd, &c, 1);
		    close(fd);
		} else {
		    fd = open("/proc/acpi/toshiba/lcd", O_RDWR);
		    if (fd >= 0) {
			char str[MAX_TOSHIBA_STRING];
			
			snprintf(str,MAX_TOSHIBA_STRING-1,"brightness : %d",toshibalcd_val);
			write(fd,str,strlen(str));	
			close(fd);
		    }
		}
        	setuid(getuid());	// drop all priority asap
		exit(0);
	} else
	// CPUFreq support
	if (strncmp(argv[i], "--cpufreq", 9) == 0 || strncmp(argv[i], "-cpufreq", 8) == 0) {
		if ((i+1) >= argc)
			break;
		if (strlen(argv[i+1]) > 50 || strchr(argv[i+1], '.'))
			break;
		int val;
		char tmp[256];
		// CPUFreq support for the interface of the 2.4 kernell (/proc/sys/cpu/N/)
		if (strcmp(argv[i], "--cpufreq-24") == 0 || strcmp(argv[i], "-cpufreq-24") == 0) {
			++i;
			snprintf(tmp, sizeof(tmp), "/proc/sys/cpu/%s/speed", argv[i]);
			tmp[sizeof(tmp)-1] = 0;
			++i;
			if (i >= argc)
				break;
			val = atoi(argv[i]);
			if (val < 0)
				break;
			fd = open(tmp, O_WRONLY);
			if (fd < 0)
				exit(1);
			snprintf(tmp, sizeof(tmp), "%d", val);
			write(fd, tmp, strlen(tmp));
		} else
		// CPUFreq support for the interface of the 2.5 kernel (/proc/cpufreq)
		if (strcmp(argv[i], "--cpufreq-25") == 0 || strcmp(argv[i], "-cpufreq-25") == 0) {
			++i;
			snprintf(tmp, sizeof(tmp), "%s", argv[i]);
			tmp[sizeof(tmp)-1] = 0;
			fd = open("/proc/cpufreq", O_WRONLY);
			if (fd < 0)
				exit(1);
			write(fd, tmp, strlen(tmp));
		} else
		// CPUFreq support fot the sysfs interface of the 2.5 kernel (/sys/devices/sys/cpuN/cpufreq/)
		if (strcmp(argv[i], "--cpufreq-sysfs") == 0 || strcmp(argv[i], "-cpufreq-sysfs") == 0) {
			++i;
			snprintf(tmp, sizeof(tmp), "/sys/devices/system/cpu/%s/cpufreq/scaling_governor", argv[i]);
			tmp[sizeof(tmp)-1] = 0;
			++i;
			if (i >= argc)
				break;
			fd = open(tmp, O_WRONLY);
			if (fd < 0)
				exit(1);
			if (strlen(argv[i]) > 50)
				break;
			snprintf(tmp, sizeof(tmp), "%s", argv[i]);
			tmp[sizeof(tmp)-1] = 0;
			write(fd, tmp, strlen(tmp));
		} else {
			break;
		}
		close(fd);
		setuid(getuid()); // drop all priority asap
		exit(0);
	} else {
usage:
        	setuid(getuid());	// drop all priority asap
		fprintf(stderr, "Usage: %s [--suspend] [--standby] [--hibernate][--software-suspend][--toshibalcd N][--performance CPU N][--throttling CPU N][--cpufreq-[24|25|sysfs]]\n", argv[0]);
		exit(1);
	}
	goto usage;
}
