1/*
2 * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) and
3 * geoffrey hing <ghing@net.ohio-state.edu>
4 * Licensed under the GPL
5 */
6
7#include <errno.h>
8#include <string.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <unistd.h>
12#include <sys/time.h>
13#include "init.h"
14#include "user.h"
15#include "kern_util.h"
16#include "os.h"
17
18#define TTY_LOG_DIR "./"
19
20/* Set early in boot and then unchanged */
21static char *tty_log_dir = TTY_LOG_DIR;
22static int tty_log_fd = -1;
23
24#define TTY_LOG_OPEN 1
25#define TTY_LOG_CLOSE 2
26#define TTY_LOG_WRITE 3
27#define TTY_LOG_EXEC 4
28
29#define TTY_READ 1
30#define TTY_WRITE 2
31
32struct tty_log_buf {
33	int what;
34	unsigned long tty;
35	int len;
36	int direction;
37	unsigned long sec;
38	unsigned long usec;
39};
40
41int open_tty_log(void *tty, void *current_tty)
42{
43	struct timeval tv;
44	struct tty_log_buf data;
45	char buf[strlen(tty_log_dir) + sizeof("01234567890-01234567\0")];
46	int fd;
47
48	gettimeofday(&tv, NULL);
49	if(tty_log_fd != -1){
50		data = ((struct tty_log_buf) { .what 	= TTY_LOG_OPEN,
51					       .tty  = (unsigned long) tty,
52					       .len  = sizeof(current_tty),
53					       .direction = 0,
54					       .sec = tv.tv_sec,
55					       .usec = tv.tv_usec } );
56		write(tty_log_fd, &data, sizeof(data));
57		write(tty_log_fd, &current_tty, data.len);
58		return tty_log_fd;
59	}
60
61	sprintf(buf, "%s/%0u-%0u", tty_log_dir, (unsigned int) tv.tv_sec,
62 		(unsigned int) tv.tv_usec);
63
64	fd = os_open_file(buf, of_append(of_create(of_rdwr(OPENFLAGS()))),
65			  0644);
66	if(fd < 0){
67		printk("open_tty_log : couldn't open '%s', errno = %d\n",
68		       buf, -fd);
69	}
70	return fd;
71}
72
73void close_tty_log(int fd, void *tty)
74{
75	struct tty_log_buf data;
76	struct timeval tv;
77
78	if(tty_log_fd != -1){
79		gettimeofday(&tv, NULL);
80		data = ((struct tty_log_buf) { .what 	= TTY_LOG_CLOSE,
81					       .tty  = (unsigned long) tty,
82					       .len  = 0,
83					       .direction = 0,
84					       .sec = tv.tv_sec,
85					       .usec = tv.tv_usec } );
86		write(tty_log_fd, &data, sizeof(data));
87		return;
88	}
89	os_close_file(fd);
90}
91
92static int log_chunk(int fd, const char *buf, int len)
93{
94	int total = 0, try, missed, n;
95	char chunk[64];
96
97	while(len > 0){
98		try = (len > sizeof(chunk)) ? sizeof(chunk) : len;
99		missed = copy_from_user_proc(chunk, (char *) buf, try);
100		try -= missed;
101		n = write(fd, chunk, try);
102		if(n != try) {
103			if(n < 0)
104				return -errno;
105			return -EIO;
106		}
107		if(missed != 0)
108			return -EFAULT;
109
110		len -= try;
111		total += try;
112		buf += try;
113	}
114
115	return total;
116}
117
118int write_tty_log(int fd, const char *buf, int len, void *tty, int is_read)
119{
120	struct timeval tv;
121	struct tty_log_buf data;
122	int direction;
123
124	if(fd == tty_log_fd){
125		gettimeofday(&tv, NULL);
126		direction = is_read ? TTY_READ : TTY_WRITE;
127		data = ((struct tty_log_buf) { .what 	= TTY_LOG_WRITE,
128					       .tty  = (unsigned long) tty,
129					       .len  = len,
130					       .direction = direction,
131					       .sec = tv.tv_sec,
132					       .usec = tv.tv_usec } );
133		write(tty_log_fd, &data, sizeof(data));
134	}
135
136	return log_chunk(fd, buf, len);
137}
138
139void log_exec(char **argv, void *tty)
140{
141	struct timeval tv;
142	struct tty_log_buf data;
143	char **ptr,*arg;
144	int len;
145
146	if(tty_log_fd == -1) return;
147
148	gettimeofday(&tv, NULL);
149
150	len = 0;
151	for(ptr = argv; ; ptr++){
152		if(copy_from_user_proc(&arg, ptr, sizeof(arg)))
153			return;
154		if(arg == NULL) break;
155		len += strlen_user_proc(arg);
156	}
157
158	data = ((struct tty_log_buf) { .what 	= TTY_LOG_EXEC,
159				       .tty  = (unsigned long) tty,
160				       .len  = len,
161				       .direction = 0,
162				       .sec = tv.tv_sec,
163				       .usec = tv.tv_usec } );
164	write(tty_log_fd, &data, sizeof(data));
165
166	for(ptr = argv; ; ptr++){
167		if(copy_from_user_proc(&arg, ptr, sizeof(arg)))
168			return;
169		if(arg == NULL) break;
170		log_chunk(tty_log_fd, arg, strlen_user_proc(arg));
171	}
172}
173
174extern void register_tty_logger(int (*opener)(void *, void *),
175				int (*writer)(int, const char *, int,
176					      void *, int),
177				void (*closer)(int, void *));
178
179static int register_logger(void)
180{
181	register_tty_logger(open_tty_log, write_tty_log, close_tty_log);
182	return 0;
183}
184
185__uml_initcall(register_logger);
186
187static int __init set_tty_log_dir(char *name, int *add)
188{
189	tty_log_dir = name;
190	return 0;
191}
192
193__uml_setup("tty_log_dir=", set_tty_log_dir,
194"tty_log_dir=<directory>\n"
195"    This is used to specify the directory where the logs of all pty\n"
196"    data from this UML machine will be written.\n\n"
197);
198
199static int __init set_tty_log_fd(char *name, int *add)
200{
201	char *end;
202
203	tty_log_fd = strtoul(name, &end, 0);
204	if((*end != '\0') || (end == name)){
205		printf("set_tty_log_fd - strtoul failed on '%s'\n", name);
206		tty_log_fd = -1;
207	}
208
209	*add = 0;
210	return 0;
211}
212
213__uml_setup("tty_log_fd=", set_tty_log_fd,
214"tty_log_fd=<fd>\n"
215"    This is used to specify a preconfigured file descriptor to which all\n"
216"    tty data will be written.  Preconfigure the descriptor with something\n"
217"    like '10>tty_log tty_log_fd=10'.\n\n"
218);
219