Blame ui/irqbalance-ui.c

Packit 9eaa09
Packit 9eaa09
#include <ctype.h>
Packit 9eaa09
#include <errno.h>
Packit 9eaa09
#include <netdb.h>
Packit 9eaa09
#include <stdio.h>
Packit 9eaa09
#include <string.h>
Packit 9eaa09
#include <stdlib.h>
Packit 9eaa09
#include <sys/socket.h>
Packit 9eaa09
#include <sys/types.h>
Packit 9eaa09
#include <sys/un.h>
Packit 9eaa09
#include <unistd.h>
Packit 9eaa09
#include <curses.h>
Packit 9eaa09
#include <ncurses.h>
Packit 9eaa09
#include "irqbalance-ui.h"
Packit 9eaa09
#include "ui.h"
Packit 9eaa09
#include "helpers.h"
Packit 9eaa09
Packit 9eaa09
Packit 9eaa09
int irqbalance_pid = -1;
Packit 9eaa09
GList *tree = NULL;
Packit 9eaa09
setup_t setup;
Packit 9eaa09
GMainLoop *main_loop;
Packit 9eaa09
int is_tree = 1;
Packit 9eaa09
Packit 9eaa09
struct msghdr * create_credentials_msg()
Packit 9eaa09
{
Packit 9eaa09
	struct ucred *credentials = malloc(sizeof(struct ucred));
Packit 9eaa09
	credentials->pid = getpid();
Packit 9eaa09
	credentials->uid = geteuid();
Packit 9eaa09
	credentials->gid = getegid();
Packit 9eaa09
Packit 9eaa09
	struct msghdr *msg = malloc(sizeof(struct msghdr));
Packit 9eaa09
	memset(msg, 0, sizeof(struct msghdr));
Packit 9eaa09
	msg->msg_iovlen = 1;
Packit 9eaa09
	msg->msg_control = malloc(CMSG_SPACE(sizeof(struct ucred)));
Packit 9eaa09
	msg->msg_controllen = CMSG_SPACE(sizeof(struct ucred));
Packit 9eaa09
Packit 9eaa09
	struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg);
Packit 9eaa09
	cmsg->cmsg_level = SOL_SOCKET;
Packit 9eaa09
	cmsg->cmsg_type = SCM_CREDENTIALS;
Packit 9eaa09
	cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
Packit 9eaa09
	memcpy(CMSG_DATA(cmsg), credentials, sizeof(struct ucred));
Packit 9eaa09
Packit Service a93a75
	free(credentials);
Packit 9eaa09
	return msg;
Packit 9eaa09
}
Packit 9eaa09
Packit 9eaa09
int init_connection()
Packit 9eaa09
{
Packit 9eaa09
	struct sockaddr_un addr;
Packit 9eaa09
	memset(&addr, 0, sizeof(struct sockaddr_un));
Packit 9eaa09
Packit 9eaa09
	int socket_fd = socket(AF_LOCAL, SOCK_STREAM, 0);
Packit 9eaa09
	if(socket_fd < 0) {
Packit 9eaa09
		perror("Error opening socket");
Packit 9eaa09
		return 0;
Packit 9eaa09
	}
Packit 9eaa09
	addr.sun_family = AF_UNIX;
Packit 9eaa09
	char socket_name[64];
Packit 9eaa09
	snprintf(socket_name, 64, "%s%d.sock", SOCKET_PATH, irqbalance_pid);
Packit 9eaa09
	strncpy(addr.sun_path + 1, socket_name, strlen(socket_name));
Packit 9eaa09
Packit 9eaa09
	if(connect(socket_fd, (struct sockaddr *)&addr,
Packit 9eaa09
				sizeof(sa_family_t) + strlen(socket_name) + 1) < 0) {
Packit 9eaa09
		return 0;
Packit 9eaa09
	}
Packit 9eaa09
Packit 9eaa09
	return socket_fd;
Packit 9eaa09
}
Packit 9eaa09
Packit 9eaa09
void send_settings(char *data)
Packit 9eaa09
{
Packit 9eaa09
	/* Send "settings sleep X" to set sleep interval, "settings ban
Packit 9eaa09
	 * irqs X Y..." to ban IRQs from balancing,
Packit 9eaa09
	 * "settings cpus <banned_list>" to setup which CPUs are forbidden
Packit 9eaa09
	 * to handle IRQs
Packit 9eaa09
	 */
Packit 9eaa09
	int socket_fd = init_connection();
Packit 9eaa09
	if(!socket_fd) {
Packit 9eaa09
		return;
Packit 9eaa09
	}
Packit 9eaa09
Packit 9eaa09
	struct msghdr *msg = create_credentials_msg();
Packit 9eaa09
	struct iovec iov;
Packit 9eaa09
	iov.iov_base = (void *) data;
Packit 9eaa09
	iov.iov_len = strlen(data);
Packit 9eaa09
	msg->msg_iov = &iov;
Packit 9eaa09
	sendmsg(socket_fd, msg, 0);
Packit 9eaa09
Packit 9eaa09
	close(socket_fd);
Packit Service a93a75
	free(msg->msg_control);
Packit Service a93a75
	free(msg);
Packit 9eaa09
}
Packit 9eaa09
Packit 9eaa09
char * get_data(char *string)
Packit 9eaa09
{
Packit 9eaa09
	/* Send "setup" to get sleep interval, banned IRQs and banned CPUs,
Packit 9eaa09
	 * "stats" to get CPU tree statistics
Packit 9eaa09
	 */
Packit 9eaa09
	int socket_fd = init_connection();
Packit 9eaa09
	if(!socket_fd) {
Packit 9eaa09
		return NULL;
Packit 9eaa09
	}
Packit 9eaa09
Packit 9eaa09
	struct msghdr *msg = create_credentials_msg();
Packit 9eaa09
	struct iovec iov;
Packit 9eaa09
	iov.iov_base = (void *) string;
Packit 9eaa09
	iov.iov_len = strlen(string);
Packit 9eaa09
	msg->msg_iov = &iov;
Packit 9eaa09
	sendmsg(socket_fd, msg, 0);
Packit 9eaa09
Packit 9eaa09
	/*
Packit 9eaa09
	 * This is just...horrible.  Mental note to replace this
Packit 9eaa09
	 * With a select, ioctl to determine size, and malloc based
Packit 9eaa09
	 * on that
Packit 9eaa09
	 */
Packit 9eaa09
	char *data = malloc(8192);
Packit 9eaa09
	int len = recv(socket_fd, data, 8192, 0);
Packit 9eaa09
	close(socket_fd);
Packit 9eaa09
	data[len] = '\0';
Packit Service a93a75
	free(msg->msg_control);
Packit Service a93a75
	free(msg);
Packit 9eaa09
	return data;
Packit 9eaa09
}
Packit 9eaa09
Packit 9eaa09
void parse_setup(char *setup_data)
Packit 9eaa09
{
Packit 9eaa09
	char *token, *ptr;
Packit 9eaa09
	int i,j;
Packit 9eaa09
	char *copy;
Packit Service a93a75
	irq_t *new_irq = NULL;
Packit 9eaa09
	if((setup_data == NULL) || (strlen(setup_data) == 0)) return;
Packit 9eaa09
	copy = strdup(setup_data);
Packit 9eaa09
	if (!copy)
Packit 9eaa09
		return;
Packit 9eaa09
Packit 9eaa09
	setup.banned_irqs = NULL;
Packit 9eaa09
	setup.banned_cpus = NULL;
Packit 9eaa09
	token = strtok_r(copy, " ", &ptr);
Packit 9eaa09
	if(strncmp(token, "SLEEP", strlen("SLEEP"))) goto out;
Packit 9eaa09
	setup.sleep = strtol(strtok_r(NULL, " ", &ptr), NULL, 10);
Packit 9eaa09
	token = strtok_r(NULL, " ", &ptr);
Packit 9eaa09
	/* Parse banned IRQ data */
Packit 9eaa09
	while(!strncmp(token, "IRQ", strlen("IRQ"))) {
Packit Service a93a75
		new_irq = malloc(sizeof(irq_t));
Packit 9eaa09
		new_irq->vector = strtol(strtok_r(NULL, " ", &ptr), NULL, 10);
Packit 9eaa09
		token = strtok_r(NULL, " ", &ptr);
Packit 9eaa09
		if(strncmp(token, "LOAD", strlen("LOAD"))) goto out;
Packit 9eaa09
		new_irq->load = strtol(strtok_r(NULL, " ", &ptr), NULL, 10);
Packit 9eaa09
		token = strtok_r(NULL, " ", &ptr);
Packit 9eaa09
		if(strncmp(token, "DIFF", strlen("DIFF"))) goto out;
Packit 9eaa09
		new_irq->diff = strtol(strtok_r(NULL, " ", &ptr), NULL, 10);
Packit 9eaa09
		token = strtok_r(ptr, " ", &ptr);
Packit 9eaa09
		if(strncmp(token, "CLASS", strlen("CLASS"))) goto out;
Packit 9eaa09
		new_irq->class = strtol(strtok_r(NULL, " ", &ptr), NULL, 10);
Packit 9eaa09
		new_irq->is_banned = 1;
Packit 9eaa09
		new_irq->assigned_to = NULL;
Packit 9eaa09
		setup.banned_irqs = g_list_append(setup.banned_irqs, new_irq);
Packit 9eaa09
		token = strtok_r(NULL, " ", &ptr);
Packit Service a93a75
		new_irq = NULL;
Packit 9eaa09
	}
Packit 9eaa09
Packit 9eaa09
	if(strncmp(token, "BANNED", strlen("BANNED"))) goto out;
Packit 9eaa09
	token = strtok_r(NULL, " ", &ptr);
Packit 9eaa09
	for(i = strlen(token) - 1; i >= 0; i--) {
Packit 9eaa09
		char *map = hex_to_bitmap(token[i]);
Packit 9eaa09
		for(j = 3; j >= 0; j--) {
Packit 9eaa09
			if(map[j] == '1') {
Packit 9eaa09
				uint64_t *banned_cpu = malloc(sizeof(uint64_t));
Packit 9eaa09
				*banned_cpu = (4 * (strlen(token) - (i + 1)) + (4 - (j + 1)));
Packit 9eaa09
				setup.banned_cpus = g_list_append(setup.banned_cpus,
Packit 9eaa09
								banned_cpu);
Packit 9eaa09
			}
Packit 9eaa09
		}
Packit Service a93a75
		free(map);
Packit 9eaa09
	
Packit 9eaa09
	}
Packit 9eaa09
	free(copy);
Packit 9eaa09
	return;
Packit 9eaa09
Packit 9eaa09
out: {
Packit 9eaa09
	/* Invalid data presented */
Packit 9eaa09
	printf("Invalid data sent.  Unexpected token: %s", token);
Packit Service a93a75
	if (new_irq) {
Packit Service a93a75
		free(new_irq);
Packit Service a93a75
	}
Packit 9eaa09
	free(copy);
Packit 9eaa09
	g_list_free(tree);
Packit 9eaa09
	exit(1);
Packit 9eaa09
}
Packit 9eaa09
}
Packit 9eaa09
Packit 9eaa09
GList * concat_child_lists(cpu_node_t *node)
Packit 9eaa09
{
Packit 9eaa09
	GList *new = NULL;
Packit 9eaa09
	GList *child_entry = g_list_first(node->children);
Packit 9eaa09
	do {
Packit 9eaa09
		cpu_node_t *child = (cpu_node_t *)child_entry->data;
Packit 9eaa09
		GList *cpu_entry = g_list_first(child->cpu_list);
Packit 9eaa09
		do {
Packit 9eaa09
			uint64_t *cpu = (uint64_t *)cpu_entry->data;
Packit 9eaa09
			new = g_list_append(new, cpu);
Packit 9eaa09
			cpu_entry = g_list_next(cpu_entry);
Packit 9eaa09
		} while(cpu_entry != NULL);
Packit 9eaa09
		child_entry = g_list_next(child_entry);
Packit 9eaa09
	} while(child_entry != NULL);
Packit 9eaa09
Packit 9eaa09
	return new;
Packit 9eaa09
}
Packit 9eaa09
Packit 9eaa09
void copy_cpu_list_to_irq(irq_t *irq, void *data)
Packit 9eaa09
{
Packit 9eaa09
	irq->assigned_to = g_list_copy((GList *)data);
Packit 9eaa09
	irq->assigned_to = g_list_sort(irq->assigned_to, sort_ints);
Packit 9eaa09
}
Packit 9eaa09
Packit 9eaa09
void assign_cpu_lists(cpu_node_t *node, void *data __attribute__((unused)))
Packit 9eaa09
{
Packit 9eaa09
	if(g_list_length(node->children) > 0) {
Packit 9eaa09
		for_each_node(node->children, assign_cpu_lists, NULL);
Packit 9eaa09
		node->cpu_list = concat_child_lists(node);
Packit 9eaa09
	} else {
Packit 9eaa09
		node->cpu_list = g_list_append(node->cpu_list, &(node->number));
Packit 9eaa09
	}
Packit 9eaa09
Packit 9eaa09
	for_each_irq(node->irqs, copy_cpu_list_to_irq, node->cpu_list);
Packit 9eaa09
}
Packit 9eaa09
Packit 9eaa09
void assign_cpu_mask(cpu_node_t *node, void *data __attribute__((unused)))
Packit 9eaa09
{
Packit 9eaa09
	char *mask = malloc(16 * sizeof(char));
Packit 9eaa09
	mask[0] = '\0';
Packit 9eaa09
	unsigned int sum = 0;
Packit 9eaa09
	GList *list_entry = g_list_first(node->cpu_list);
Packit 9eaa09
	do {
Packit 9eaa09
		int *cpu = list_entry->data;
Packit 9eaa09
		sum += 1 << (*cpu);
Packit 9eaa09
		list_entry = g_list_next(list_entry);
Packit 9eaa09
	} while(list_entry != NULL);
Packit 9eaa09
	snprintf(mask, 15, "0x%x", sum);
Packit 9eaa09
	node->cpu_mask = mask;
Packit 9eaa09
Packit 9eaa09
	if(g_list_length(node->children) > 0) {
Packit 9eaa09
		for_each_node(node->children, assign_cpu_mask, NULL);
Packit 9eaa09
	}
Packit 9eaa09
}
Packit 9eaa09
Packit 9eaa09
void parse_into_tree(char *data)
Packit 9eaa09
{
Packit 9eaa09
	char *token, *ptr;
Packit 9eaa09
	cpu_node_t *parent = NULL;
Packit 9eaa09
	char *copy;
Packit 9eaa09
	tree = NULL;
Packit Service a93a75
	irq_t *new_irq = NULL;
Packit Service a93a75
	cpu_node_t *new = NULL;
Packit Service a93a75
Packit 9eaa09
	if (!data || strlen(data) == 0)
Packit 9eaa09
		return;
Packit 9eaa09
Packit 9eaa09
	copy = strdup(data);
Packit 9eaa09
	if (!copy)
Packit 9eaa09
		return;
Packit 9eaa09
Packit 9eaa09
	token = strtok_r(copy, " ", &ptr);
Packit 9eaa09
	while(token != NULL) {
Packit 9eaa09
		/* Parse node data */
Packit 9eaa09
		if(strncmp(token, "TYPE", strlen("TYPE"))) {
Packit 9eaa09
			free(copy);
Packit 9eaa09
			 goto out;
Packit 9eaa09
		}
Packit Service a93a75
		new = malloc(sizeof(cpu_node_t));
Packit 9eaa09
		new->irqs = NULL;
Packit 9eaa09
		new->children = NULL;
Packit 9eaa09
		new->cpu_list = NULL;
Packit 9eaa09
		new->cpu_mask = NULL;
Packit 9eaa09
		new->type = strtol(strtok_r(NULL, " ", &ptr), NULL, 10);
Packit 9eaa09
		if(new->type == OBJ_TYPE_NODE) {
Packit 9eaa09
			parent = NULL;
Packit 9eaa09
		} else if(new->type >= parent->type) {
Packit 9eaa09
			parent = parent->parent;
Packit 9eaa09
		}
Packit 9eaa09
		token = strtok_r(NULL, " ", &ptr);
Packit 9eaa09
		if(strncmp(token, "NUMBER", strlen("NUMBER"))) goto out;
Packit 9eaa09
		new->number = strtol(strtok_r(NULL, " ", &ptr), NULL, 10);
Packit 9eaa09
		token = strtok_r(NULL, " ", &ptr);
Packit 9eaa09
		if(strncmp(token, "LOAD", strlen("LOAD"))) goto out;
Packit 9eaa09
		new->load = strtol(strtok_r(NULL, " ", &ptr), NULL, 10);
Packit 9eaa09
		token = strtok_r(NULL, " ", &ptr);
Packit 9eaa09
		if(strncmp(token, "SAVE_MODE", strlen("SAVE_MODE"))) goto out;
Packit 9eaa09
		new->is_powersave = strtol(strtok_r(NULL, " ", &ptr), NULL, 10);
Packit 9eaa09
		token = strtok_r(NULL, " ", &ptr);
Packit 9eaa09
Packit 9eaa09
		/* Parse assigned IRQ data */
Packit 9eaa09
		while((token != NULL) && (!strncmp(token, "IRQ", strlen("IRQ")))) {
Packit Service a93a75
			new_irq = malloc(sizeof(irq_t));
Packit 9eaa09
			new_irq->vector = strtol(strtok_r(NULL, " ", &ptr), NULL, 10);
Packit 9eaa09
			token = strtok_r(NULL, " ", &ptr);
Packit 9eaa09
			if(strncmp(token, "LOAD", strlen("LOAD"))) goto out;
Packit 9eaa09
			new_irq->load = strtol(strtok_r(NULL, " ", &ptr), NULL, 10);
Packit 9eaa09
			token = strtok_r(NULL, " ", &ptr);
Packit 9eaa09
			if(strncmp(token, "DIFF", strlen("DIFF"))) goto out;
Packit 9eaa09
			new_irq->diff = strtol(strtok_r(NULL, " ", &ptr), NULL, 10);
Packit 9eaa09
			token = strtok_r(NULL, " ", &ptr);
Packit 9eaa09
			if(strncmp(token, "CLASS", strlen("CLASS"))) goto out;
Packit 9eaa09
			new_irq->class = strtol(strtok_r(NULL, " ", &ptr), NULL, 10);
Packit 9eaa09
			new_irq->is_banned = 0;
Packit 9eaa09
			new->irqs = g_list_append(new->irqs, new_irq);
Packit 9eaa09
			token = strtok_r(NULL, " ", &ptr);
Packit Service a93a75
			new_irq = NULL;
Packit 9eaa09
		}
Packit 9eaa09
Packit 9eaa09
		if((token == NULL) || (strncmp(token, "IRQ", strlen("IRQ")))) {
Packit 9eaa09
			new->parent = parent;
Packit 9eaa09
			if(parent == NULL) {
Packit 9eaa09
				tree = g_list_append(tree, new);
Packit 9eaa09
			} else {
Packit 9eaa09
				parent->children = g_list_append(parent->children, new);
Packit 9eaa09
			}
Packit 9eaa09
			if(new->type != OBJ_TYPE_CPU) {
Packit 9eaa09
				parent = new;
Packit 9eaa09
			}
Packit 9eaa09
		}
Packit Service a93a75
Packit Service a93a75
		new = NULL;
Packit 9eaa09
	}
Packit 9eaa09
	free(copy);
Packit 9eaa09
	for_each_node(tree, assign_cpu_lists, NULL);
Packit 9eaa09
	for_each_node(tree, assign_cpu_mask, NULL);
Packit 9eaa09
	return;
Packit 9eaa09
Packit 9eaa09
out: {
Packit 9eaa09
	/* Invalid data presented */
Packit 9eaa09
	printf("Invalid data sent.  Unexpected token: %s\n", token);
Packit Service a93a75
	if (new_irq) {
Packit Service a93a75
		free(new_irq);
Packit Service a93a75
	}
Packit Service a93a75
	if (new) {
Packit Service a93a75
		free(new);
Packit Service a93a75
	}
Packit 9eaa09
	g_list_free(tree);
Packit 9eaa09
	exit(1);
Packit 9eaa09
}
Packit 9eaa09
}
Packit 9eaa09
Packit 9eaa09
gboolean rescan_tree(gpointer data __attribute__((unused)))
Packit 9eaa09
{
Packit 9eaa09
	char *setup_data = get_data(SETUP);
Packit 9eaa09
	parse_setup(setup_data);
Packit 9eaa09
	char *irqbalance_data = get_data(STATS);
Packit 9eaa09
	parse_into_tree(irqbalance_data);
Packit 9eaa09
	if(is_tree) {
Packit 9eaa09
		display_tree();
Packit 9eaa09
	}
Packit 9eaa09
	free(setup_data);
Packit Service a93a75
	free(irqbalance_data);
Packit 9eaa09
	return TRUE;
Packit 9eaa09
}
Packit 9eaa09
Packit 9eaa09
gboolean key_loop(gpointer data __attribute__((unused)))
Packit 9eaa09
{
Packit 9eaa09
	int c = getch();
Packit 9eaa09
	switch(c) {
Packit 9eaa09
	case 'q':
Packit 9eaa09
		close_window(0);
Packit 9eaa09
		break;
Packit 9eaa09
	case KEY_F(3):
Packit 9eaa09
		is_tree = 1;
Packit 9eaa09
		display_tree();
Packit 9eaa09
		break;
Packit 9eaa09
	case KEY_F(4):
Packit 9eaa09
		is_tree = 0;
Packit 9eaa09
		settings();
Packit 9eaa09
		break;
Packit 9eaa09
	case KEY_F(5):
Packit 9eaa09
		is_tree = 0;
Packit 9eaa09
		setup_irqs();
Packit 9eaa09
		break;
Packit 9eaa09
	default:
Packit 9eaa09
		break;
Packit 9eaa09
	}
Packit 9eaa09
	return TRUE;
Packit 9eaa09
}
Packit 9eaa09
Packit 9eaa09
int main(int argc, char **argv)
Packit 9eaa09
{
Packit 9eaa09
	if(getuid() != 0) {
Packit 9eaa09
		printf("This program needs to be executed with root priviledges\n");
Packit 9eaa09
		return EACCES;
Packit 9eaa09
	}
Packit 9eaa09
	if(argc > 1) {
Packit 9eaa09
		/* PID of irqbalance specified */
Packit 9eaa09
		irqbalance_pid = strtol(argv[1], NULL, 10);
Packit 9eaa09
		if(!irqbalance_pid) {
Packit 9eaa09
			printf("PID must be a number\n");
Packit 9eaa09
			return EINVAL;
Packit 9eaa09
		}
Packit 9eaa09
	} else {
Packit 9eaa09
		/* We need to find irqbalance's PID */
Packit 9eaa09
		DIR *dir = opendir("/proc");
Packit 9eaa09
		if(dir) {
Packit 9eaa09
			struct dirent *entry;
Packit 9eaa09
			char cmdfile[512];
Packit 9eaa09
			char cmdstring[256];
Packit 9eaa09
			cmdstring[255] = '\0';
Packit 9eaa09
			do {
Packit 9eaa09
				entry = readdir(dir);
Packit 9eaa09
				if(entry) {
Packit 9eaa09
					snprintf(cmdfile, 512, "/proc/%s/cmdline", entry->d_name);
Packit 9eaa09
					FILE *f = fopen(cmdfile, "r");
Packit 9eaa09
					if(f == NULL) {
Packit 9eaa09
						continue;
Packit 9eaa09
					}
Packit 9eaa09
					fgets(cmdstring, 255, f);
Packit 9eaa09
					if((strstr(cmdstring, "irqbalance") != NULL) &&
Packit 9eaa09
							(strstr(cmdstring, "irqbalance-ui") == NULL)) {
Packit 9eaa09
						irqbalance_pid = strtol(entry->d_name, NULL, 10);
Packit 9eaa09
					}
Packit 9eaa09
					fclose(f);
Packit 9eaa09
				}
Packit 9eaa09
			} while((entry) && (irqbalance_pid == -1));
Packit 9eaa09
		}
Packit 9eaa09
		if(irqbalance_pid == -1) {
Packit 9eaa09
			printf("Unable to determine irqbalance PID\n");
Packit 9eaa09
			return EINVAL;
Packit 9eaa09
		}
Packit 9eaa09
	}
Packit 9eaa09
Packit 9eaa09
	init();
Packit 9eaa09
Packit 9eaa09
	main_loop = g_main_loop_new(NULL, FALSE);
Packit 9eaa09
	g_timeout_add_seconds(5, rescan_tree, NULL);
Packit 9eaa09
	g_timeout_add_seconds(1, key_loop, NULL);
Packit 9eaa09
	g_main_loop_run(main_loop);
Packit 9eaa09
Packit 9eaa09
Packit 9eaa09
	g_main_loop_quit(main_loop);
Packit 9eaa09
	close_window(0);
Packit 9eaa09
	return 0;
Packit 9eaa09
}