root/kernel/chr_drv/tty_ioctl.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. flush
  2. flush_input
  3. flush_output
  4. wait_until_sent
  5. do_get_ps_info
  6. get_termios
  7. check_change
  8. set_termios
  9. get_termio
  10. set_termio
  11. set_window_size
  12. get_window_size
  13. tty_set_ldisc
  14. tty_ioctl

   1 /*
   2  *  linux/kernel/chr_drv/tty_ioctl.c
   3  *
   4  *  Copyright (C) 1991, 1992  Linus Torvalds
   5  *
   6  * Modified by Fred N. van Kempen, 01/29/93, to add line disciplines
   7  * which can be dynamically activated and de-activated by the line
   8  * discipline handling modules (like SLIP).
   9  */
  10 
  11 #include <linux/types.h>
  12 #include <linux/termios.h>
  13 #include <linux/errno.h>
  14 #include <linux/sched.h>
  15 #include <linux/config.h>
  16 #include <linux/kernel.h>
  17 #include <linux/tty.h>
  18 #include <linux/fcntl.h>
  19 
  20 #include <asm/io.h>
  21 #include <asm/segment.h>
  22 #include <asm/system.h>
  23 
  24 #undef  DEBUG
  25 #ifdef DEBUG
  26 # define        PRINTK(x)       printk (x)
  27 #else
  28 # define        PRINTK(x)       /**/
  29 #endif
  30 
  31 extern int session_of_pgrp(int pgrp);
  32 extern int do_screendump(int arg);
  33 extern int kill_pg(int pgrp, int sig, int priv);
  34 
  35 #ifdef CONFIG_SELECTION
  36 extern int set_selection(const int arg);
  37 extern int paste_selection(struct tty_struct *tty);
  38 #endif /* CONFIG_SELECTION */
  39 
  40 static void flush(struct tty_queue * queue)
     /* [previous][next][first][last][top][bottom][index][help] */
  41 {
  42         if (queue) {
  43                 cli();
  44                 queue->head = queue->tail;
  45                 sti();
  46                 wake_up_interruptible(&queue->proc_list);
  47         }
  48 }
  49 
  50 void flush_input(struct tty_struct * tty)
     /* [previous][next][first][last][top][bottom][index][help] */
  51 {
  52         tty->status_changed = 1;
  53         tty->ctrl_status |= TIOCPKT_FLUSHREAD;
  54         flush(&tty->read_q);
  55         wake_up_interruptible(&tty->read_q.proc_list);
  56         flush(&tty->secondary);
  57         tty->secondary.data = 0;
  58 
  59         if ((tty = tty->link) != NULL) {
  60                 flush(&tty->write_q);
  61                 wake_up_interruptible(&tty->write_q.proc_list);
  62         }
  63 }
  64 
  65 void flush_output(struct tty_struct * tty)
     /* [previous][next][first][last][top][bottom][index][help] */
  66 {
  67         tty->status_changed = 1;
  68         tty->ctrl_status |= TIOCPKT_FLUSHWRITE;
  69         flush(&tty->write_q);
  70         wake_up_interruptible(&tty->write_q.proc_list);
  71         if ((tty = tty->link) != NULL) {
  72                 flush(&tty->read_q);
  73                 wake_up_interruptible(&tty->read_q.proc_list);
  74                 flush(&tty->secondary);
  75                 tty->secondary.data = 0;
  76         }
  77 }
  78 
  79 void wait_until_sent(struct tty_struct * tty)
     /* [previous][next][first][last][top][bottom][index][help] */
  80 {
  81         struct wait_queue wait = { current, NULL };
  82 
  83         TTY_WRITE_FLUSH(tty);
  84         if (EMPTY(&tty->write_q))
  85                 return;
  86         add_wait_queue(&tty->write_q.proc_list, &wait);
  87         current->counter = 0;   /* make us low-priority */
  88         while (1) {
  89                 current->state = TASK_INTERRUPTIBLE;
  90                 if (current->signal & ~current->blocked)
  91                         break;
  92                 TTY_WRITE_FLUSH(tty);
  93                 if (EMPTY(&tty->write_q))
  94                         break;
  95                 schedule();
  96         }
  97         current->state = TASK_RUNNING;
  98         remove_wait_queue(&tty->write_q.proc_list, &wait);
  99 }
 100 
 101 static int do_get_ps_info(int arg)
     /* [previous][next][first][last][top][bottom][index][help] */
 102 {
 103         struct tstruct {
 104                 int flag;
 105                 int present[NR_TASKS];
 106                 struct task_struct tasks[NR_TASKS];
 107         };
 108         struct tstruct *ts = (struct tstruct *)arg;
 109         struct task_struct **p;
 110         char *c, *d;
 111         int i, n = 0;
 112         
 113         i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct tstruct));
 114         if (i)
 115                 return i;
 116         for (p = &FIRST_TASK ; p <= &LAST_TASK ; p++, n++)
 117                 if (*p)
 118                 {
 119                         c = (char *)(*p);
 120                         d = (char *)(ts->tasks+n);
 121                         for (i=0 ; i<sizeof(struct task_struct) ; i++)
 122                                 put_fs_byte(*c++, d++);
 123                         put_fs_long(1, (unsigned long *)(ts->present+n));
 124                 }
 125                 else    
 126                         put_fs_long(0, (unsigned long *)(ts->present+n));
 127         return(0);                      
 128 }
 129 
 130 static int get_termios(struct tty_struct * tty, struct termios * termios)
     /* [previous][next][first][last][top][bottom][index][help] */
 131 {
 132         int i;
 133 
 134         i = verify_area(VERIFY_WRITE, termios, sizeof (*termios));
 135         if (i)
 136                 return i;
 137         for (i=0 ; i< (sizeof (*termios)) ; i++)
 138                 put_fs_byte( ((char *)tty->termios)[i] , i+(char *)termios );
 139         return 0;
 140 }
 141 
 142 static int check_change(struct tty_struct * tty, int channel)
     /* [previous][next][first][last][top][bottom][index][help] */
 143 {
 144         /* If we try to set the state of terminal and we're not in the
 145            foreground, send a SIGTTOU.  If the signal is blocked or
 146            ignored, go ahead and perform the operation.  POSIX 7.2) */
 147         if (current->tty != channel)
 148                 return 0;
 149         if (tty->pgrp <= 0 || tty->pgrp == current->pgrp)
 150                 return 0;
 151         if (is_orphaned_pgrp(current->pgrp))
 152                 return -EIO;
 153         if (is_ignored(SIGTTOU))
 154                 return 0;
 155         (void) kill_pg(current->pgrp,SIGTTOU,1);
 156         return -ERESTARTSYS;
 157 }
 158 
 159 static int set_termios(struct tty_struct * tty, struct termios * termios,
     /* [previous][next][first][last][top][bottom][index][help] */
 160                         int channel)
 161 {
 162         int i;
 163         struct termios old_termios = *tty->termios;
 164 
 165         i = check_change(tty, channel);
 166         if (i)
 167                 return i;
 168         for (i=0 ; i< (sizeof (*termios)) ; i++)
 169                 ((char *)tty->termios)[i]=get_fs_byte(i+(char *)termios);
 170 
 171         /* puting mpty's into echo mode is very bad, and I think under
 172            some situations can cause the kernel to do nothing but
 173            copy characters back and forth. -RAB */
 174         if (IS_A_PTY_MASTER(channel)) tty->termios->c_lflag &= ~ECHO;
 175 
 176         if (tty->set_termios)
 177                 (*tty->set_termios)(tty, &old_termios);
 178 
 179         return 0;
 180 }
 181 
 182 static int get_termio(struct tty_struct * tty, struct termio * termio)
     /* [previous][next][first][last][top][bottom][index][help] */
 183 {
 184         int i;
 185         struct termio tmp_termio;
 186 
 187         i = verify_area(VERIFY_WRITE, termio, sizeof (*termio));
 188         if (i)
 189                 return i;
 190         tmp_termio.c_iflag = tty->termios->c_iflag;
 191         tmp_termio.c_oflag = tty->termios->c_oflag;
 192         tmp_termio.c_cflag = tty->termios->c_cflag;
 193         tmp_termio.c_lflag = tty->termios->c_lflag;
 194         tmp_termio.c_line = tty->termios->c_line;
 195         for(i=0 ; i < NCC ; i++)
 196                 tmp_termio.c_cc[i] = tty->termios->c_cc[i];
 197         for (i=0 ; i< (sizeof (*termio)) ; i++)
 198                 put_fs_byte( ((char *)&tmp_termio)[i] , i+(char *)termio );
 199         return 0;
 200 }
 201 
 202 /*
 203  * This only works as the 386 is low-byte-first
 204  */
 205 static int set_termio(struct tty_struct * tty, struct termio * termio,
     /* [previous][next][first][last][top][bottom][index][help] */
 206                         int channel)
 207 {
 208         int i;
 209         struct termio tmp_termio;
 210         struct termios old_termios = *tty->termios;
 211 
 212         i = check_change(tty, channel);
 213         if (i)
 214                 return i;
 215         for (i=0 ; i< (sizeof (*termio)) ; i++)
 216                 ((char *)&tmp_termio)[i]=get_fs_byte(i+(char *)termio);
 217 
 218         /* take care of the packet stuff. */
 219         if ((tmp_termio.c_iflag & IXON) &&
 220             !(tty->termios->c_iflag & IXON))
 221           {
 222              tty->status_changed = 1;
 223              tty->ctrl_status |= TIOCPKT_DOSTOP;
 224           }
 225 
 226         if ((tty->termios->c_iflag & IXON) &&
 227             !(tmp_termio.c_iflag & IXON))
 228           {
 229              tty->status_changed = 1;
 230              tty->ctrl_status |= TIOCPKT_NOSTOP;
 231           }
 232 
 233         *(unsigned short *)&tty->termios->c_iflag = tmp_termio.c_iflag;
 234         *(unsigned short *)&tty->termios->c_oflag = tmp_termio.c_oflag;
 235         *(unsigned short *)&tty->termios->c_cflag = tmp_termio.c_cflag;
 236         *(unsigned short *)&tty->termios->c_lflag = tmp_termio.c_lflag;
 237         tty->termios->c_line = tmp_termio.c_line;
 238         for(i=0 ; i < NCC ; i++)
 239                 tty->termios->c_cc[i] = tmp_termio.c_cc[i];
 240 
 241         if (tty->set_termios)
 242                 (*tty->set_termios)(tty, &old_termios);
 243 
 244         return 0;
 245 }
 246 
 247 static int set_window_size(struct tty_struct * tty, struct winsize * ws)
     /* [previous][next][first][last][top][bottom][index][help] */
 248 {
 249         int i,changed;
 250         char c, * tmp;
 251 
 252         if (!ws)
 253                 return -EINVAL;
 254         tmp = (char *) &tty->winsize;
 255         changed = 0;
 256         for (i = 0; i < sizeof (*ws) ; i++,tmp++) {
 257                 c = get_fs_byte(i + (char *) ws);
 258                 if (c == *tmp)
 259                         continue;
 260                 changed = 1;
 261                 *tmp = c;
 262         }
 263         if (changed)
 264                 kill_pg(tty->pgrp, SIGWINCH, 1);
 265         return 0;
 266 }
 267 
 268 static int get_window_size(struct tty_struct * tty, struct winsize * ws)
     /* [previous][next][first][last][top][bottom][index][help] */
 269 {
 270         int i;
 271         char * tmp;
 272 
 273         if (!ws)
 274                 return -EINVAL;
 275         i = verify_area(VERIFY_WRITE, ws, sizeof (*ws));
 276         if (i)
 277                 return i;
 278         tmp = (char *) ws;
 279         for (i = 0; i < sizeof (struct winsize) ; i++,tmp++)
 280                 put_fs_byte(((char *) &tty->winsize)[i], tmp);
 281         return 0;
 282 }
 283 
 284 /* Set the discipline of a tty line. */
 285 static int tty_set_ldisc(struct tty_struct *tty, int ldisc)
     /* [previous][next][first][last][top][bottom][index][help] */
 286 {
 287         if ((ldisc < N_TTY) || (ldisc >= NR_LDISCS) ||
 288             !(ldiscs[ldisc].flags & LDISC_FLAG_DEFINED))
 289                 return -EINVAL;
 290 
 291         if (tty->disc == ldisc)
 292                 return 0;       /* We are already in the desired discipline */
 293 
 294         /* Shutdown the current discipline. */
 295         wait_until_sent(tty);
 296         flush_input(tty);
 297         if (ldiscs[tty->disc].close)
 298                 ldiscs[tty->disc].close(tty);
 299 
 300         /* Now set up the new line discipline. */
 301         tty->disc = ldisc;
 302         if (ldiscs[tty->disc].open)
 303                 return(ldiscs[tty->disc].open(tty));
 304         else
 305                 return 0;
 306 }
 307 
 308 
 309 int tty_ioctl(struct inode * inode, struct file * file,
     /* [previous][next][first][last][top][bottom][index][help] */
 310         unsigned int cmd, unsigned long arg)
 311 {
 312         struct tty_struct * tty;
 313         struct tty_struct * other_tty;
 314         struct tty_struct * termios_tty;
 315         int pgrp;
 316         int dev;
 317         int termios_dev;
 318         int retval;
 319 
 320         if (MAJOR(file->f_rdev) != 4) {
 321                 printk("tty_ioctl: tty pseudo-major != 4\n");
 322                 return -EINVAL;
 323         }
 324         dev = MINOR(file->f_rdev);
 325         tty = TTY_TABLE(dev);
 326         if (!tty)
 327                 return -EINVAL;
 328         if (IS_A_PTY(dev))
 329                 other_tty = tty_table[PTY_OTHER(dev)];
 330         else
 331                 other_tty = NULL;
 332         termios_tty = tty;
 333         termios_dev = dev;
 334         if (IS_A_PTY_MASTER(dev)) {
 335                 termios_tty = other_tty;
 336                 termios_dev = PTY_OTHER(dev);
 337         }
 338         switch (cmd) {
 339                 case TCGETS:
 340                         return get_termios(termios_tty,(struct termios *) arg);
 341                 case TCSETSF:
 342                         flush_input(tty);
 343                 /* fallthrough */
 344                 case TCSETSW:
 345                         wait_until_sent(tty);
 346                 /* fallthrough */
 347                 case TCSETS:
 348                         return set_termios(termios_tty,(struct termios *) arg, termios_dev);
 349                 case TCGETA:
 350                         return get_termio(termios_tty,(struct termio *) arg);
 351                 case TCSETAF:
 352                         flush_input(tty);
 353                 /* fallthrough */
 354                 case TCSETAW:
 355                         wait_until_sent(tty); /* fallthrough */
 356                 case TCSETA:
 357                         return set_termio(termios_tty,(struct termio *) arg, termios_dev);
 358                 case TCXONC:
 359                         switch (arg) {
 360                         case TCOOFF:
 361                                 tty->stopped = 1;
 362                                 TTY_WRITE_FLUSH(tty);
 363                                 return 0;
 364                         case TCOON:
 365                                 tty->stopped = 0;
 366                                 TTY_WRITE_FLUSH(tty);
 367                                 return 0;
 368                         case TCIOFF:
 369                                 if (STOP_CHAR(tty))
 370                                         put_tty_queue(STOP_CHAR(tty),
 371                                                       &tty->write_q);
 372                                 return 0;
 373                         case TCION:
 374                                 if (START_CHAR(tty))
 375                                         put_tty_queue(START_CHAR(tty),
 376                                                       &tty->write_q);
 377                                 return 0;
 378                         }
 379                         return -EINVAL; /* not implemented */
 380                 case TCFLSH:
 381                         if (arg==0)
 382                                 flush_input(tty);
 383                         else if (arg==1)
 384                                 flush_output(tty);
 385                         else if (arg==2) {
 386                                 flush_input(tty);
 387                                 flush_output(tty);
 388                         } else
 389                                 return -EINVAL;
 390                         return 0;
 391                 case TIOCEXCL:
 392                         return -EINVAL; /* not implemented */
 393                 case TIOCNXCL:
 394                         return -EINVAL; /* not implemented */
 395                 case TIOCSCTTY:
 396                         if ((current->leader && current->tty < 0 &&
 397                              tty->session == 0) ||
 398                             (arg == 1 && suser())) {
 399                                 current->tty = dev;
 400                                 tty->session = current->session;
 401                                 tty->pgrp = current->pgrp;
 402                                 return 0;
 403                         }
 404                         return -EPERM;
 405                 case TIOCGPGRP:
 406                         retval = verify_area(VERIFY_WRITE, (void *) arg,4);
 407                         if (!retval)
 408                                 put_fs_long(termios_tty->pgrp,(unsigned long *) arg);
 409                         return retval;
 410                 case TIOCSPGRP:
 411                         if ((current->tty < 0) ||
 412                             (current->tty != termios_dev) ||
 413                             (termios_tty->session != current->session))
 414                                 return -ENOTTY;
 415                         pgrp=get_fs_long((unsigned long *) arg);
 416                         if (pgrp < 0)
 417                                 return -EINVAL;
 418                         if (session_of_pgrp(pgrp) != current->session)
 419                                 return -EPERM;
 420                         termios_tty->pgrp = pgrp;                       
 421                         return 0;
 422                 case TIOCOUTQ:
 423                         retval = verify_area(VERIFY_WRITE, (void *) arg,4);
 424                         if (!retval)
 425                                 put_fs_long(CHARS(&tty->write_q),
 426                                     (unsigned long *) arg);
 427                         return retval;
 428                 case TIOCINQ:
 429                         retval = verify_area(VERIFY_WRITE, (void *) arg,4);
 430                         if (retval)
 431                                 return retval;
 432                         if (L_CANON(tty) && !tty->secondary.data)
 433                                 put_fs_long(0, (unsigned long *) arg);
 434                         else
 435                                 put_fs_long(CHARS(&tty->secondary),
 436                                         (unsigned long *) arg);
 437                         return 0;
 438                 case TIOCSTI:
 439                         return -EINVAL; /* not implemented */
 440                 case TIOCGWINSZ:
 441                         return get_window_size(tty,(struct winsize *) arg);
 442                 case TIOCSWINSZ:
 443                         if (IS_A_PTY_MASTER(dev))
 444                                 set_window_size(other_tty,(struct winsize *) arg);
 445                         return set_window_size(tty,(struct winsize *) arg);
 446                 case TIOCGSOFTCAR:
 447                         return -EINVAL; /* not implemented */
 448                 case TIOCSSOFTCAR:
 449                         return -EINVAL; /* not implemented */
 450                 case TIOCLINUX:
 451                         switch (get_fs_byte((char *)arg))
 452                         {
 453                                 case 0: 
 454                                         return do_screendump(arg);
 455                                 case 1: 
 456                                         return do_get_ps_info(arg);
 457 #ifdef CONFIG_SELECTION
 458                                 case 2:
 459                                         return set_selection(arg);
 460                                 case 3:
 461                                         return paste_selection(tty);
 462 #endif /* CONFIG_SELECTION */
 463                                 default: 
 464                                         return -EINVAL;
 465                         }
 466                 case TIOCCONS:
 467                         if (IS_A_CONSOLE(dev)) {
 468                                 if (!suser())
 469                                         return -EPERM;
 470                                 redirect = NULL;
 471                                 return 0;
 472                         }
 473                         if (redirect)
 474                                 return -EBUSY;
 475                         if (!suser())
 476                                 return -EPERM;
 477                         if (IS_A_PTY_MASTER(dev))
 478                                 redirect = other_tty;
 479                         else if (IS_A_PTY_SLAVE(dev))
 480                                 redirect = tty;
 481                         else
 482                                 return -EINVAL;
 483                         return 0;
 484                 case FIONBIO:
 485                         arg = get_fs_long((unsigned long *) arg);
 486                         if (arg)
 487                                 file->f_flags |= O_NONBLOCK;
 488                         else
 489                                 file->f_flags &= ~O_NONBLOCK;
 490                         return 0;
 491                 case TIOCNOTTY:
 492                         if (MINOR(file->f_rdev) != current->tty)
 493                                 return -EINVAL;
 494                         current->tty = -1;
 495                         if (current->leader) {
 496                                 if (tty->pgrp > 0)
 497                                         kill_pg(tty->pgrp, SIGHUP, 0);
 498                                 tty->pgrp = -1;
 499                                 tty->session = 0;
 500                         }
 501                         return 0;
 502                 case TIOCGETD:
 503                         retval = verify_area(VERIFY_WRITE, (void *) arg,4);
 504                         if (!retval)
 505                                 put_fs_long(tty->disc, (unsigned long *) arg);
 506                         return retval;
 507                 case TIOCSETD:
 508                         arg = get_fs_long((unsigned long *) arg);
 509                         return tty_set_ldisc(tty, arg);
 510                case TIOCPKT:
 511                         {
 512                            int on;
 513                            if (!IS_A_PTY_MASTER(dev))
 514                              return -EINVAL;
 515                            retval = verify_area(VERIFY_READ, (unsigned long *)arg, sizeof (int));
 516                            if (retval)
 517                                 return retval;
 518                            on=get_fs_long ((unsigned long *)arg);
 519                            if (on )
 520                              tty->packet = 1;
 521                            else
 522                              tty->packet = 0;
 523                            return (0);
 524                         }
 525 
 526                 default:
 527                         if (tty->ioctl) {
 528                                 retval = (tty->ioctl)(tty, file, cmd, arg);
 529                                 if (retval != -EINVAL)
 530                                         return retval;
 531                         }
 532                         if (ldiscs[tty->disc].ioctl) {
 533                                 retval = (ldiscs[tty->disc].ioctl)
 534                                         (tty, file, cmd, arg);
 535                                 return retval;
 536                         }
 537                         return -EINVAL;
 538         }
 539 }

/* [previous][next][first][last][top][bottom][index][help] */