summaryrefslogtreecommitdiff
path: root/utils/gdmopen.c
blob: 43571baa9473dc6831ed0d84e9be8149f04cc7a9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
/*
 * 	 gdmopen.c by the Queen of England, based upon original open.
 *	 Simplified for the purposes of gdm.  All useless (to me)
 *	 functionality stripped.  Also returns what the command returns.
 *	 Return of 66 means error with open.
 *
 *	 Original header:
 *
 *       open.c open a vt to run a new command (or shell).
 *       
 *	 Copyright (c) 1994 by Jon Tombs <jon@gtex02.us.es>
 *
 *       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.
 */

#include "config.h"
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <dirent.h>
#include <pwd.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/vt.h>
#include <sys/types.h>
#include <sys/wait.h>

#ifndef FALSE
#define FALSE 0
#define TRUE !FALSE
#endif


/*
 * Where your VTs are hidden
 */
#ifdef __linux__
#define VTNAME "/dev/tty%d"
#endif

#ifdef ESIX_5_3_2_D
#define	VTBASE		"/dev/vt%02d"
#endif

const char *GDMOPENversion = "gdmopen " VERSION " simplified (was: open: 1.4 (c) Jon Tombs 1994)";

#ifndef VTNAME
#error vt device name must be defined in open.c
#endif

static pid_t child_pid = -1; 
struct vt_stat vt;
static int vtno;
static int fd = 0;
static int do_switchback = FALSE;

static void
sighandler (int sig)
{
	if (child_pid > 1) {
		if (kill (child_pid, sig) == 0)
			waitpid (child_pid, NULL, 0);
	}

	if (do_switchback) {
		/* Switch back... */
		(void) ioctl(fd, VT_ACTIVATE, vt.v_active);
		/* wait to be really sure we have switched */
		(void) ioctl(fd, VT_WAITACTIVE, vt.v_active);
	}

	/* Kill myself with this signal */
	signal (sig, SIG_DFL);
	kill (getpid (), sig);
}

int 
main (int argc, char *argv[])
{
	char vtname[256];
	int status;
	int cmd_start = 1;
	char *command = NULL;

	if (getuid () != geteuid () ||
	    getuid () != 0) {
		fprintf (stderr, "gdmopen: Only root wants to run me\n");
		return 66;
	}

	signal (SIGTERM, sighandler);
	signal (SIGINT, sighandler);
	signal (SIGHUP, sighandler);

	if (argc <= 1) {
		fprintf (stderr, "gdmopen: must supply a command!\n");
		return 66;
	}

	command = argv[1];

	if (strcmp (argv[1], "-l") == 0) {
		char *p;
		if (argc <= 2) {
			fprintf (stderr, "gdmopen: must supply a command!\n");
			return 66;
		}
		/* prepend '-' and start the command at
		 * argument 2 */
		cmd_start = 2;
		command = argv[2];
		argv[2] = malloc (strlen (command) + 2);
		p = strrchr (command, '/');
		if (p != NULL) {
			/* make it "-basename" */
			strcpy (argv[2]+1, p+1);
		} else {
			strcpy (argv[2]+1, command);
		}
		*(argv[2]) = '-';
	}

	fd = open ("/dev/console", O_WRONLY, 0);
	if (fd < 0) {
		perror ("gdmopen: Failed to open /dev/console");	
		return 66;
	}

	errno = 0;
	if ((ioctl(fd, VT_OPENQRY, &vtno) < 0) || (vtno == -1)) {
		perror ("gdmopen: Cannot find a free VT");
		close (fd);
		return 66;
	}

	if (ioctl(fd, VT_GETSTATE, &vt) < 0) {
		perror ("gdmopen: can't get VTstate");
		close(fd);
		return 66;
	}

	snprintf (vtname, sizeof (vtname), VTNAME, vtno);

	chown (vtname, 0, -1);

	child_pid = fork();
	if (child_pid == 0) {
		char VT_NUMBER[256];

		snprintf (VT_NUMBER, sizeof (VT_NUMBER), "VT_NUMBER=%d", vtno);
		putenv (VT_NUMBER);

		signal (SIGTERM, SIG_DFL);
		signal (SIGINT, SIG_DFL);
		signal (SIGHUP, SIG_DFL);

		/* leave current vt */
		if (
#ifdef   ESIX_5_3_2_D
		setpgrp() < 0
#else
		setsid() < 0
#endif      
		) {
			fprintf(stderr, "open: Unable to set new session (%s)\n",
				strerror(errno));
		}
		close(0);
		close(1);
		close(2);
		close(fd);	 

		/* and grab new one */
		fd = open (vtname, O_RDWR);
		if (fd < 0) { /* Shouldn't happen */
			_exit (66); /* silently die */
		}
		dup(fd);
		dup(fd);

		/* 
		* Can't tell anyone if any of these fail, so throw away
		* the return values 
		 */
		(void) ioctl(fd, VT_ACTIVATE, vtno);
		/* wait to be really sure we have switched */
		(void) ioctl(fd, VT_WAITACTIVE, vtno);

#ifdef __linux__
		/* Turn on fonts */
		write (0, "\033(K", 3);
#endif /* __linux__ */

		execvp (command, &argv[cmd_start]);

		_exit (66); /* failed */
	}

	if (child_pid < 0) {
		perror ("gdmopen: fork() error");
		return 66;
	}

	do_switchback = TRUE;

	waitpid (child_pid, &status, 0);
	child_pid = -1;

	do_switchback = FALSE;

	/* Switch back... */
	(void) ioctl(fd, VT_ACTIVATE, vt.v_active);
	/* wait to be really sure we have switched */
	(void) ioctl(fd, VT_WAITACTIVE, vt.v_active);

	close (fd);

	if (WIFEXITED (status))
		return WEXITSTATUS (status);
	else
		return 66;
}