File: new.c

package info (click to toggle)
vlock 2.2.2-5
  • links: PTS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 488 kB
  • ctags: 324
  • sloc: ansic: 2,950; sh: 873; makefile: 236
file content (233 lines) | stat: -rw-r--r-- 6,097 bytes parent folder | download | duplicates (7)
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
230
231
232
233
/* new.c -- console allocation plugin for vlock,
 *          the VT locking program for linux
 *
 * This program is copyright (C) 2007 Frank Benkstein, and is free
 * software which is freely distributable under the terms of the
 * GNU General Public License version 2, included as the file COPYING in this
 * distribution.  It is NOT public domain software, and any
 * redistribution not permitted by the GNU General Public License is
 * expressly forbidden without prior written permission from
 * the author.
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <errno.h>

#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
#include <sys/consio.h>
#else
#include <sys/vt.h>
#endif

#include "vlock_plugin.h"

const char *preceeds[] = { "all", NULL };
const char *requires[] = { "all", NULL };

/* name of the virtual console device */
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
#define CONSOLE "/dev/ttyv0"
#else
#define CONSOLE "/dev/tty0"
#endif
/* template for the device of a given virtual console */
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
#define VTNAME "/dev/ttyv%x"
#else
#define VTNAME "/dev/tty%d"
#endif

/* Get the currently active console from the given
 * console file descriptor.  Returns console number
 * (starting from 1) on success, -1 on error. */
#if defined(__FreeBSD__) || defined (__FreeBSD_kernel__)
static int get_active_console(int consfd)
{
  int n;

  if (ioctl(consfd, VT_GETACTIVE, &n) == 0)
    return n;
  else
    return -1;
}
#else
static int get_active_console(int consfd)
{
  struct vt_stat vtstate;

  /* get the virtual console status */
  if (ioctl(consfd, VT_GETSTATE, &vtstate) == 0)
    return vtstate.v_active;
  else
    return -1;
}
#endif

/* Get the device name for the given console number.
 * Returns the device name or NULL on error. */
static char *get_console_name(int n)
{
  static char name[sizeof VTNAME + 2];
  ssize_t namelen;

  if (n <= 0)
    return NULL;

  /* format the virtual terminal filename from the number */
#if defined(__FreeBSD__) || defined (__FreeBSD_kernel__)
  namelen = snprintf(name, sizeof name, VTNAME, n - 1);
#else
  namelen = snprintf(name, sizeof name, VTNAME, n);
#endif

  if (namelen > (ssize_t) sizeof name) {
    fprintf(stderr, "vlock-new: virtual terminal number too large\n");
    return NULL;
  } else if (namelen < 0) {
    fprintf(stderr, "vlock-new: error calculating terminal device name: %s\n", strerror(errno));
    return NULL;
  } else {
    return name;
  }
}

/* Change to the given console number using the given console
 * file descriptor. */
static int activate_console(int consfd, int vtno)
{
  int c = ioctl(consfd, VT_ACTIVATE, vtno);

  return c < 0 ? c : ioctl(consfd, VT_WAITACTIVE, vtno);
}

struct new_console_context {
  int consfd;
  int old_vtno;
  int new_vtno;
  int saved_stdin;
  int saved_stdout;
  int saved_stderr;
};

/* Run switch to a new console and redirect stdio there. */
bool vlock_start(void **ctx_ptr)
{
  struct new_console_context *ctx;
  int vtfd;
  char *vtname;

  /* Allocate the context. */
  if ((ctx = malloc(sizeof *ctx)) == NULL)
    return false;

  /* Try stdin first. */
  ctx->consfd = dup(STDIN_FILENO);

  /* Get the number of the currently active console. */
  ctx->old_vtno = get_active_console(ctx->consfd);

  if (ctx->old_vtno < 0) {
    /* stdin is does not a virtual console. */
    (void) close(ctx->consfd);

    /* XXX: add optional PAM check here */

    /* Open the virtual console directly. */
    if ((ctx->consfd = open(CONSOLE, O_RDWR)) < 0) {
      perror("vlock-new: cannot open virtual console");
      goto err;
    }

    /* Get the number of the currently active console, again. */
    ctx->old_vtno = get_active_console(ctx->consfd);

    if (ctx->old_vtno < 0) {
      perror("vlock-new: could not get the currently active console");
      goto err;
    }
  }

  /* Get a free virtual terminal number. */
  if (ioctl(ctx->consfd, VT_OPENQRY, &ctx->new_vtno) < 0) {
    perror("vlock-new: could not find a free virtual terminal");
    goto err;
  }

  /* Get the device name for the new virtual console. */
  vtname = get_console_name(ctx->new_vtno);

  /* Open the free virtual terminal. */
  if ((vtfd = open(vtname, O_RDWR)) < 0) {
    perror("vlock-new: cannot open new console");
    goto err;
  }

  /* Work around stupid X11 bug:  When switching immediately after the command
   * is entered, the enter button may get stuck. */
  if (getenv("DISPLAY") != NULL)
    sleep(1);

  /* Switch to the new virtual terminal. */
  if (activate_console(ctx->consfd, ctx->new_vtno) < 0) {
    perror("vlock-new: could not activate new terminal");
    goto err;
  }

  /* Save the stdio file descriptors. */
  ctx->saved_stdin = dup(STDIN_FILENO);
  ctx->saved_stdout = dup(STDOUT_FILENO);
  ctx->saved_stderr = dup(STDERR_FILENO);

  /* Redirect stdio to virtual terminal. */
  (void) dup2(vtfd, STDIN_FILENO);
  (void) dup2(vtfd, STDOUT_FILENO);
  (void) dup2(vtfd, STDERR_FILENO);

  /* Close virtual terminal file descriptor. */
  (void) close(vtfd);

  *ctx_ptr = ctx;
  return true;

err:
  errno = 0;
  free(ctx);
  return false;
}

/* Redirect stdio back und switch to the previous console. */
bool vlock_end(void **ctx_ptr)
{
  struct new_console_context *ctx = *ctx_ptr;

  if (ctx == NULL)
    return true;

  /* Restore saved stdio file descriptors. */
  (void) dup2(ctx->saved_stdin, STDIN_FILENO);
  (void) dup2(ctx->saved_stdout, STDOUT_FILENO);
  (void) dup2(ctx->saved_stderr, STDERR_FILENO);

  /* Switch back to previous virtual terminal. */
  if (activate_console(ctx->consfd, ctx->old_vtno) < 0)
    perror("vlock-new: could not activate previous console");

#if !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__)
  /* Deallocate virtual terminal. */
  if (ioctl(ctx->consfd, VT_DISALLOCATE, ctx->new_vtno) < 0)
    perror("vlock-new: could not disallocate console");
#endif

  (void) close(ctx->consfd);
  free(ctx);

  return true;
}