~ubuntu-branches/ubuntu/karmic/iterm/karmic

« back to all changes in this revision

Viewing changes to lib/src/unix/tty/ttyio.c

  • Committer: Bazaar Package Importer
  • Author(s): Anthony Fok
  • Date: 2004-02-27 04:13:16 UTC
  • Revision ID: james.westby@ubuntu.com-20040227041316-q0jn37sia8mt0t9u
Tags: upstream-0.5
ImportĀ upstreamĀ versionĀ 0.5

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* This software is subject to the terms of the Common Public License
 
2
   You must accept the terms of this license to use this software.
 
3
 
 
4
   Copyright (C) 2002, International Business Machines Corporation
 
5
   and others.  All Rights Reserved.
 
6
 
 
7
   Further information about Common Public License Version 0.5 is obtained
 
8
   from url http://oss.software.ibm.com/developer/opensource/license-cpl.html
 
9
*/
 
10
#include "../../../config.h"
 
11
 
 
12
#include "iterm/unix/ttyio.h"
 
13
#include "iterm/io.h"
 
14
#include <termios.h>
 
15
#include <sys/ioctl.h>
 
16
#include <sys/types.h>
 
17
#include <sys/stat.h>
 
18
#include <sys/wait.h>
 
19
#include <fcntl.h>
 
20
#define __USE_XOPEN
 
21
#include <stdlib.h>
 
22
#undef __USE_XOPEN
 
23
#include <unistd.h>
 
24
#include <stropts.h>
 
25
 
 
26
#include <string.h>
 
27
#include <strings.h>
 
28
#include <signal.h>
 
29
#include <errno.h>
 
30
#include <utmpx.h>
 
31
#include <pwd.h>
 
32
 
 
33
#if defined(HAVE_LIBUTEMPTER)
 
34
#include <utempter.h>
 
35
#endif
 
36
 
 
37
/*
 
38
 * ConcreteIO definitin, which is for pseudo tty base communication
 
39
 */
 
40
struct Concrete_IO
 
41
{
 
42
  int fd;
 
43
      /* file descriptor of this object */
 
44
  char slave_name[20];
 
45
      /* slave tty name */
 
46
 
 
47
  pid_t child;
 
48
      /* child process id */
 
49
  unsigned closed:1;
 
50
      /* flag if this file descriptor is closed or not */
 
51
};
 
52
 
 
53
#if defined(HAVE_LIBUTEMPTER)
 
54
#define write_utmp(tty_name,fd) addToUtmp(tty_name,NULL,fd);
 
55
#define delete_utmp(tty_name) removeFromUtmp();
 
56
#else
 
57
static void write_utmp(char *tty_name)
 
58
{
 
59
  struct utmpx entry;
 
60
  struct passwd *pw;
 
61
  char id[4];
 
62
  char *p;
 
63
  char *line;
 
64
  char *host;
 
65
 
 
66
  p = rindex(tty_name,'/') + 1;
 
67
  if( '0' <= *p && *p <= '9')
 
68
  {
 
69
        /* /dev/pty/? */
 
70
    id[0] = 'p';
 
71
    strncpy(id+1,p,2);
 
72
    line = tty_name + strlen("/dev/");
 
73
  }
 
74
  else
 
75
  {
 
76
        /* /dev/ttyp? */
 
77
    strncpy(id,p,3);
 
78
    line = p;
 
79
  }
 
80
  id[3] = '\0';
 
81
  memset(&entry,0,sizeof(entry));
 
82
  entry.ut_type = DEAD_PROCESS;
 
83
  strncpy(entry.ut_id,id,sizeof(id));
 
84
  setutent();
 
85
  getutxid(&entry);
 
86
  pw = getpwuid(getuid());
 
87
  entry.ut_type = USER_PROCESS;
 
88
  entry.ut_pid = getpid();
 
89
  strncpy(entry.ut_line, line, sizeof(entry.ut_line));
 
90
  strncpy(entry.ut_user, pw->pw_name, sizeof(entry.ut_user));
 
91
 
 
92
  host = getenv("DISPLAY");
 
93
  if(host)
 
94
    strncpy(entry.ut_host, host, sizeof(entry.ut_host));
 
95
 
 
96
  entry.ut_tv.tv_sec = time(NULL);
 
97
  entry.ut_tv.tv_usec = 0;
 
98
  pututxline(&entry);
 
99
  
 
100
  endutxent();
 
101
}
 
102
static void delete_utmp(char *tty_name)
 
103
{
 
104
  struct utmpx entry;
 
105
  struct utmpx *utp;
 
106
  char id[3];
 
107
  char *p;
 
108
  char *line;
 
109
 
 
110
  p = rindex(tty_name,'/') + 1;
 
111
  if( '0' <= *p && *p <= '9')
 
112
  {
 
113
        /* /dev/pty/? */
 
114
    id[0] = 'p';
 
115
    strncpy(id+1,p,2);
 
116
    line = rindex(p,'/') + 1;
 
117
  }
 
118
  else
 
119
  {
 
120
        /* /dev/ttyp? */
 
121
    strncpy(id,p,3);
 
122
    line = p;
 
123
  }
 
124
  memset(&entry,0,sizeof(entry));
 
125
  entry.ut_type = USER_PROCESS;
 
126
  strncpy(entry.ut_id,id,sizeof(id));
 
127
  setutxent();
 
128
  utp = getutxid(&entry);
 
129
  if(utp)
 
130
  {
 
131
    utp->ut_type = DEAD_PROCESS;
 
132
    memset(utp->ut_user,0,sizeof(entry.ut_user));
 
133
    entry.ut_tv.tv_sec = time(NULL);
 
134
    entry.ut_tv.tv_usec = 0;
 
135
    
 
136
    pututxline(utp);
 
137
  }
 
138
  endutxent();
 
139
}
 
140
#endif
 
141
 
 
142
static int open_master(char *slave_name)
 
143
{
 
144
  char *ptys;
 
145
  int fd;
 
146
 
 
147
  if( (fd = open("/dev/ptmx",O_RDWR)) < 0 )
 
148
      return -1;
 
149
 
 
150
  if( grantpt(fd) < 0 )
 
151
  {
 
152
    close(fd);
 
153
    return -1;
 
154
  }
 
155
 
 
156
  if( unlockpt(fd) < 0 )
 
157
  {
 
158
    close(fd);
 
159
    return -1;
 
160
  }
 
161
 
 
162
  if ( (ptys = (char *)ptsname(fd)) == NULL )
 
163
  {
 
164
    close(fd);
 
165
    return -1;
 
166
  }
 
167
 
 
168
  strcpy(slave_name,ptys);
 
169
  return fd;
 
170
}
 
171
 
 
172
static int open_slave(int master_fd, char *slave_name)
 
173
{
 
174
  int fd;
 
175
 
 
176
  if( (fd = open(slave_name,O_RDWR)) < 0 )
 
177
  {
 
178
    close(master_fd);
 
179
    return -1;
 
180
  }
 
181
 
 
182
  if ( ioctl(fd,I_FIND,"ptem") == 0)
 
183
      if ( ioctl(fd,I_PUSH,"ptem") < 0 )
 
184
      {
 
185
        close(master_fd);
 
186
        close(fd);
 
187
        return -1;
 
188
      }
 
189
  
 
190
  if ( ioctl(fd,I_FIND,"ldterm") == 0)
 
191
      if ( ioctl(fd,I_PUSH,"ldterm") < 0 )
 
192
      {
 
193
        close(master_fd);
 
194
        close(fd);
 
195
        return -1;
 
196
      }
 
197
 
 
198
  if ( ioctl(fd,I_FIND,"ttcompat") == 0)
 
199
      if ( ioctl(fd,I_PUSH,"ttcompat") < 0 )
 
200
      {
 
201
        close(master_fd);
 
202
        close(fd);
 
203
        return -1;
 
204
      }
 
205
  close(master_fd);
 
206
  return fd;
 
207
}
 
208
 
 
209
static int get_slave_fd(int master_fd, char *slave_name)
 
210
{
 
211
  int fd;
 
212
  
 
213
  if (setsid() < 0)
 
214
      return -1;
 
215
 
 
216
  if( (fd = open_slave(master_fd,slave_name)) < 0 )
 
217
      return -1;
 
218
 
 
219
  return fd;
 
220
}
 
221
 
 
222
static int set_window_size(int fd, int width, int height)
 
223
{
 
224
  struct winsize wsize;
 
225
 
 
226
  wsize.ws_col = width;
 
227
  wsize.ws_row = height;
 
228
  
 
229
  if(ioctl(fd,TIOCSWINSZ,&wsize))
 
230
      return -1;
 
231
 
 
232
  return 0;
 
233
}
 
234
 
 
235
static int dup_fds(int slave_fd)
 
236
{
 
237
 
 
238
  if (dup2(slave_fd,STDIN_FILENO) != STDIN_FILENO)
 
239
      return -1;
 
240
  
 
241
  if (dup2(slave_fd,STDOUT_FILENO) != STDOUT_FILENO)
 
242
      return -1;
 
243
  
 
244
  if (dup2(slave_fd,STDERR_FILENO) != STDERR_FILENO)
 
245
      return -1;
 
246
 
 
247
  if (slave_fd > STDERR_FILENO)
 
248
      close(slave_fd);
 
249
  
 
250
  return 0;
 
251
}
 
252
 
 
253
static pid_t child; /* child process id */
 
254
static char *tty_name; 
 
255
static int tty_read(TerminalIO *tio,char *buf,int size)
 
256
{
 
257
  int ret ;
 
258
 
 
259
  if(child == 0 || tio == NULL ||
 
260
     tio->concrete_io == NULL || tio->concrete_io->closed)
 
261
      return -1;
 
262
  ret = read(tio->concrete_io->fd,buf,size);
 
263
  if (ret < 0)
 
264
      tio->concrete_io->closed = 1;
 
265
  return ret;
 
266
}
 
267
 
 
268
static int tty_write(TerminalIO *tio,char *buf,int size)
 
269
{
 
270
  int ret;
 
271
 
 
272
  if(child == 0 || tio == NULL ||
 
273
     tio->concrete_io == NULL || tio->concrete_io->closed)
 
274
      return -1;
 
275
  ret = write(tio->concrete_io->fd,buf,size);
 
276
  if (ret < 0)
 
277
      tio->concrete_io->closed = 1;
 
278
  return ret;
 
279
}
 
280
 
 
281
static int tty_tell_window_size(TerminalIO *tio,int width, int height)
 
282
{
 
283
  if(tio == NULL || tio->concrete_io == NULL)
 
284
      return -1;
 
285
  return set_window_size(tio->concrete_io->fd,width,height);
 
286
}
 
287
 
 
288
  /* to kill child process propery, need to catch signal ;-) */
 
289
static void finish_child(int signal)
 
290
{
 
291
  pid_t pid;
 
292
  pid = wait(NULL);
 
293
 
 
294
  do {
 
295
    if(pid == child || pid == ECHILD)
 
296
    {
 
297
      child = 0;
 
298
      delete_utmp(tty_name);
 
299
      break;
 
300
    }
 
301
  } while((pid = waitpid(child,NULL,WNOHANG)) > 0);
 
302
}
 
303
 
 
304
/*
 
305
 * TTYIO  Constructor.
 
306
 */
 
307
TerminalIO* TtyTerminalIO_new(int width,int height,
 
308
                                char *program_name, char *argv[])
 
309
{
 
310
  pid_t pid;
 
311
  int master_fd;
 
312
  char slave_name[20];
 
313
  struct Concrete_IO *cio;
 
314
  TerminalIO *tio;
 
315
 
 
316
#ifdef SIGTTOU
 
317
      /* so that TIOCSWINSZ || TIOCSIZE doesn't block */
 
318
  signal(SIGTTOU,SIG_IGN);
 
319
#endif
 
320
  if ( (master_fd = open_master(slave_name)) < 0 )
 
321
      return NULL;
 
322
 
 
323
  if ( (tio = malloc(sizeof(TerminalIO))) == NULL )
 
324
      return NULL;
 
325
  
 
326
  if ( (cio = malloc(sizeof(struct Concrete_IO))) == NULL )
 
327
      return NULL;
 
328
 
 
329
  strcpy(cio->slave_name,slave_name);
 
330
  tty_name = cio->slave_name;
 
331
  
 
332
  signal(SIGCHLD,finish_child);
 
333
  if ( (pid = fork()) < 0 )
 
334
      return NULL;
 
335
 
 
336
  if(pid)
 
337
  {
 
338
    child = cio->child = pid;
 
339
    cio->fd = master_fd;
 
340
    tio->concrete_io = cio;
 
341
    tio->read = tty_read;
 
342
    tio->write = tty_write;
 
343
    tio->tell_window_size = tty_tell_window_size;
 
344
    tio->concrete_io->closed = 0;
 
345
    return tio;
 
346
  }
 
347
  else /* child */
 
348
  {
 
349
    int fd;
 
350
 
 
351
#if defined(HAVE_LIBUTEMPTER)    
 
352
    write_utmp(cio->slave_name,master_fd);
 
353
#else
 
354
    write_utmp(cio->slave_name);
 
355
#endif
 
356
    if ( (fd = get_slave_fd(master_fd,slave_name)) < 0)
 
357
      exit(1);
 
358
 
 
359
    if ( set_window_size(fd,width,height) < 0 )
 
360
      exit(1);
 
361
 
 
362
    if ( dup_fds(fd) < 0 )
 
363
      exit(1);
 
364
 
 
365
    chown(slave_name,getuid(),getgid());
 
366
    setgid(getgid());
 
367
    setuid(getuid());
 
368
    signal(SIGINT,SIG_DFL);
 
369
    signal(SIGQUIT,SIG_DFL);
 
370
    signal(SIGCHLD,SIG_DFL);
 
371
    execvp(program_name,argv);
 
372
  }
 
373
  return NULL;
 
374
}
 
375
 
 
376
/*
 
377
 * TTYIO Destructor
 
378
 */
 
379
void TtyTerminalIO_destroy(TerminalIO *tio)
 
380
{
 
381
  if(tio != NULL)
 
382
  {
 
383
    if(!tio->concrete_io->closed)
 
384
        delete_utmp(tio->concrete_io->slave_name);
 
385
    if(tio->concrete_io != NULL)
 
386
        free(tio->concrete_io);
 
387
    free(tio);
 
388
  }
 
389
}
 
390
 
 
391
/*
 
392
 * Obtain associated file descriptor
 
393
 */
 
394
int TtyTerminalIO_get_associated_fd(TerminalIO *tio)
 
395
{
 
396
  return tio->concrete_io->fd;
 
397
}