• Skip to content
  • Skip to link menu
Trinity API Reference
  • Trinity API Reference
  • tdesu
 

tdesu

  • tdesu
process.cpp
1 /*
2  *
3  * $Id$
4  *
5  * This file is part of the KDE project, module tdesu.
6  * Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
7  *
8  * This file contains code from TEShell.C of the KDE konsole.
9  * Copyright (c) 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
10  *
11  * This is free software; you can use this library under the GNU Library
12  * General Public License, version 2. See the file "COPYING.LIB" for the
13  * exact licensing terms.
14  *
15  * process.cpp: Functionality to build a front end to password asking
16  * terminal programs.
17  */
18 
19 #include <config.h>
20 
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <fcntl.h>
25 #include <signal.h>
26 #include <errno.h>
27 #include <string.h>
28 #include <termios.h>
29 #include <signal.h>
30 
31 #include <sys/types.h>
32 #include <sys/wait.h>
33 #include <sys/stat.h>
34 #include <sys/time.h>
35 #include <sys/resource.h>
36 #include <sys/ioctl.h>
37 
38 #if defined(__SVR4) && defined(sun)
39 #include <stropts.h>
40 #include <sys/stream.h>
41 #endif
42 
43 #ifdef HAVE_SYS_SELECT_H
44 #include <sys/select.h> // Needed on some systems.
45 #endif
46 
47 #include <tqglobal.h>
48 #include <tqcstring.h>
49 #include <tqfile.h>
50 
51 #include <tdeconfig.h>
52 #include <kdebug.h>
53 #include <kstandarddirs.h>
54 
55 #include "process.h"
56 #include "tdesu_pty.h"
57 #include "kcookie.h"
58 
59 int PtyProcess::waitMS(int fd,int ms)
60 {
61  struct timeval tv;
62  tv.tv_sec = 0;
63  tv.tv_usec = 1000*ms;
64 
65  fd_set fds;
66  FD_ZERO(&fds);
67  FD_SET(fd,&fds);
68  return select(fd+1, &fds, 0L, 0L, &tv);
69 }
70 
71 /*
72 ** Basic check for the existence of @p pid.
73 ** Returns true iff @p pid is an extant process.
74 */
75 bool PtyProcess::checkPid(pid_t pid)
76 {
77  TDEConfig* config = TDEGlobal::config();
78  config->setGroup("super-user-command");
79  TQString superUserCommand = config->readEntry("super-user-command", DEFAULT_SUPER_USER_COMMAND);
80  //sudo does not accept signals from user so we except it
81  if (superUserCommand == "sudo") {
82  return true;
83  } else {
84  return kill(pid,0) == 0;
85  }
86 }
87 
88 /*
89 ** Check process exit status for process @p pid.
90 ** On error (no child, no exit), return Error (-1).
91 ** If child @p pid has exited, return its exit status,
92 ** (which may be zero).
93 ** If child @p has not exited, return NotExited (-2).
94 */
95 
96 int PtyProcess::checkPidExited(pid_t pid)
97 {
98  int state, ret;
99  ret = waitpid(pid, &state, WNOHANG);
100 
101  if (ret < 0)
102  {
103  kdError(900) << k_lineinfo << "waitpid(): " << perror << "\n";
104  return Error;
105  }
106  if (ret == pid)
107  {
108  if (WIFEXITED(state))
109  return WEXITSTATUS(state);
110  return Killed;
111  }
112 
113  return NotExited;
114 }
115 
116 
117 class PtyProcess::PtyProcessPrivate
118 {
119 public:
120  QCStringList env;
121 };
122 
123 
124 PtyProcess::PtyProcess()
125 {
126  m_bTerminal = false;
127  m_bErase = false;
128  m_pPTY = 0L;
129  d = new PtyProcessPrivate;
130 }
131 
132 
133 int PtyProcess::init()
134 {
135  delete m_pPTY;
136  m_pPTY = new PTY();
137  m_Fd = m_pPTY->getpt();
138  if (m_Fd < 0)
139  return -1;
140  if ((m_pPTY->grantpt() < 0) || (m_pPTY->unlockpt() < 0))
141  {
142  kdError(900) << k_lineinfo << "Master setup failed.\n";
143  m_Fd = -1;
144  return -1;
145  }
146  m_TTY = m_pPTY->ptsname();
147  m_Inbuf.resize(0);
148  return 0;
149 }
150 
151 
152 PtyProcess::~PtyProcess()
153 {
154  delete m_pPTY;
155  delete d;
156 }
157 
159 void PtyProcess::setEnvironment( const QCStringList &env )
160 {
161  d->env = env;
162 }
163 
164 const QCStringList& PtyProcess::environment() const
165 {
166  return d->env;
167 }
168 
169 /*
170  * Read one line of input. The terminal is in canonical mode, so you always
171  * read a line at at time, but it's possible to receive multiple lines in
172  * one time.
173  */
174 
175 TQCString PtyProcess::readLine(bool block)
176 {
177  int pos;
178  TQCString ret;
179 
180  if (!m_Inbuf.isEmpty())
181  {
182  pos = m_Inbuf.find('\n');
183  if (pos == -1)
184  {
185  ret = m_Inbuf;
186  m_Inbuf.resize(0);
187  } else
188  {
189  ret = m_Inbuf.left(pos);
190  m_Inbuf = m_Inbuf.mid(pos+1);
191  }
192  return ret;
193  }
194 
195  int flags = fcntl(m_Fd, F_GETFL);
196  if (flags < 0)
197  {
198  kdError(900) << k_lineinfo << "fcntl(F_GETFL): " << perror << "\n";
199  return ret;
200  }
201  int oflags = flags;
202  if (block)
203  flags &= ~O_NONBLOCK;
204  else
205  flags |= O_NONBLOCK;
206 
207  if ((flags != oflags) && (fcntl(m_Fd, F_SETFL, flags) < 0))
208  {
209  // We get an error here when the child process has closed
210  // the file descriptor already.
211  return ret;
212  }
213 
214  int nbytes;
215  char buf[256];
216  while (1)
217  {
218  nbytes = read(m_Fd, buf, 255);
219  if (nbytes == -1)
220  {
221  if (errno == EINTR)
222  continue;
223  else break;
224  }
225  if (nbytes == 0)
226  break; // eof
227 
228  buf[nbytes] = '\000';
229  m_Inbuf += buf;
230 
231  pos = m_Inbuf.find('\n');
232  if (pos == -1)
233  {
234  ret = m_Inbuf;
235  m_Inbuf.resize(0);
236  } else
237  {
238  ret = m_Inbuf.left(pos);
239  m_Inbuf = m_Inbuf.mid(pos+1);
240  }
241  break;
242  }
243 
244  return ret;
245 }
246 
247 TQCString PtyProcess::readAll(bool block)
248 {
249  TQCString ret;
250 
251  if (!m_Inbuf.isEmpty())
252  {
253  // if there is still something in the buffer, we need not block.
254  // we should still try to read any further output, from the fd, though.
255  block = false;
256  ret = m_Inbuf;
257  m_Inbuf.resize(0);
258  }
259 
260  int flags = fcntl(m_Fd, F_GETFL);
261  if (flags < 0)
262  {
263  kdError(900) << k_lineinfo << "fcntl(F_GETFL): " << perror << "\n";
264  return ret;
265  }
266  int oflags = flags;
267  if (block)
268  flags &= ~O_NONBLOCK;
269  else
270  flags |= O_NONBLOCK;
271 
272  if ((flags != oflags) && (fcntl(m_Fd, F_SETFL, flags) < 0))
273  {
274  // We get an error here when the child process has closed
275  // the file descriptor already.
276  return ret;
277  }
278 
279  int nbytes;
280  char buf[256];
281  while (1)
282  {
283  nbytes = read(m_Fd, buf, 255);
284  if (nbytes == -1)
285  {
286  if (errno == EINTR)
287  continue;
288  else break;
289  }
290  if (nbytes == 0)
291  break; // eof
292 
293  buf[nbytes] = '\000';
294  ret += buf;
295  break;
296  }
297 
298  return ret;
299 }
300 
301 
302 void PtyProcess::writeLine(const TQCString &line, bool addnl)
303 {
304  if (!line.isEmpty())
305  write(m_Fd, line, line.length());
306  if (addnl)
307  write(m_Fd, "\n", 1);
308 }
309 
310 
311 void PtyProcess::unreadLine(const TQCString &line, bool addnl)
312 {
313  TQCString tmp = line;
314  if (addnl)
315  tmp += '\n';
316  if (!tmp.isEmpty())
317  m_Inbuf.prepend(tmp);
318 }
319 
320 /*
321  * Fork and execute the command. This returns in the parent.
322  */
323 
324 int PtyProcess::exec(const TQCString &command, const QCStringList &args)
325 {
326  kdDebug(900) << k_lineinfo << "Running `" << command << "'\n";
327 
328  if (init() < 0)
329  return -1;
330 
331  // Open the pty slave before forking. See SetupTTY()
332  int slave = open(m_TTY, O_RDWR);
333  if (slave < 0)
334  {
335  kdError(900) << k_lineinfo << "Could not open slave pty.\n";
336  return -1;
337  }
338 
339  if ((m_Pid = fork()) == -1)
340  {
341  kdError(900) << k_lineinfo << "fork(): " << perror << "\n";
342  return -1;
343  }
344 
345  // Parent
346  if (m_Pid)
347  {
348  close(slave);
349  return 0;
350  }
351 
352  // Child
353  if (SetupTTY(slave) < 0)
354  _exit(1);
355 
356  for(QCStringList::ConstIterator it = d->env.begin();
357  it != d->env.end(); it++)
358  {
359  putenv(const_cast<TQCString&>(*it).data());
360  }
361  unsetenv("TDE_FULL_SESSION");
362 
363  // set temporarily LC_ALL to C, for su (to be able to parse "Password:")
364  const char* old_lc_all = getenv( "LC_ALL" );
365  if( old_lc_all != NULL )
366  setenv( "TDESU_LC_ALL", old_lc_all, 1 );
367  else
368  unsetenv( "TDESU_LC_ALL" );
369  setenv("LC_ALL", "C", 1);
370 
371  // From now on, terminal output goes through the tty.
372 
373  TQCString path;
374  if (command.contains('/'))
375  path = command;
376  else
377  {
378  TQString file = TDEStandardDirs::findExe(command);
379  if (file.isEmpty())
380  {
381  kdError(900) << k_lineinfo << command << " not found\n";
382  _exit(1);
383  }
384  path = TQFile::encodeName(file);
385  }
386 
387  const char **argp = (const char **)malloc((args.count()+2)*sizeof(char *));
388  int i = 0;
389  argp[i++] = path;
390  for (QCStringList::ConstIterator it=args.begin(); it!=args.end(); ++it)
391  argp[i++] = *it;
392 
393  argp[i] = 0L;
394 
395  execv(path, (char * const *)argp);
396  kdError(900) << k_lineinfo << "execv(\"" << path << "\"): " << perror << "\n";
397  _exit(1);
398  return -1; // Shut up compiler. Never reached.
399 }
400 
401 
402 /*
403  * Wait until the terminal is set into no echo mode. At least one su
404  * (RH6 w/ Linux-PAM patches) sets noecho mode AFTER writing the Password:
405  * prompt, using TCSAFLUSH. This flushes the terminal I/O queues, possibly
406  * taking the password with it. So we wait until no echo mode is set
407  * before writing the password.
408  * Note that this is done on the slave fd. While Linux allows tcgetattr() on
409  * the master side, Solaris doesn't.
410  */
411 
412 int PtyProcess::WaitSlave()
413 {
414  int slave = open(m_TTY, O_RDWR);
415  if (slave < 0)
416  {
417  kdError(900) << k_lineinfo << "Could not open slave tty.\n";
418  return -1;
419  }
420 
421  kdDebug(900) << k_lineinfo << "Child pid " << m_Pid << endl;
422 
423  struct termios tio;
424  while (1)
425  {
426  if (!checkPid(m_Pid))
427  {
428  close(slave);
429  return -1;
430  }
431  if (tcgetattr(slave, &tio) < 0)
432  {
433  kdError(900) << k_lineinfo << "tcgetattr(): " << perror << "\n";
434  close(slave);
435  return -1;
436  }
437  if (tio.c_lflag & ECHO)
438  {
439  kdDebug(900) << k_lineinfo << "Echo mode still on.\n";
440  waitMS(slave,100);
441  continue;
442  }
443  break;
444  }
445  close(slave);
446  return 0;
447 }
448 
449 
450 int PtyProcess::enableLocalEcho(bool enable)
451 {
452  int slave = open(m_TTY, O_RDWR);
453  if (slave < 0)
454  {
455  kdError(900) << k_lineinfo << "Could not open slave tty.\n";
456  return -1;
457  }
458  struct termios tio;
459  if (tcgetattr(slave, &tio) < 0)
460  {
461  kdError(900) << k_lineinfo << "tcgetattr(): " << perror << "\n";
462  close(slave); return -1;
463  }
464  if (enable)
465  tio.c_lflag |= ECHO;
466  else
467  tio.c_lflag &= ~ECHO;
468  if (tcsetattr(slave, TCSANOW, &tio) < 0)
469  {
470  kdError(900) << k_lineinfo << "tcsetattr(): " << perror << "\n";
471  close(slave); return -1;
472  }
473  close(slave);
474  return 0;
475 }
476 
477 
478 /*
479  * Copy output to stdout until the child process exists, or a line of output
480  * matches `m_Exit'.
481  * We have to use waitpid() to test for exit. Merely waiting for EOF on the
482  * pty does not work, because the target process may have children still
483  * attached to the terminal.
484  */
485 
486 int PtyProcess::waitForChild()
487 {
488  int retval = 1;
489 
490  fd_set fds;
491  FD_ZERO(&fds);
492 
493  while (1)
494  {
495  int ret = 0;
496 
497  if (m_Fd != -1)
498  {
499  FD_SET(m_Fd, &fds);
500  ret = select(m_Fd+1, &fds, 0L, 0L, 0L);
501  }
502  if (ret == -1)
503  {
504  if (errno != EINTR)
505  {
506  kdError(900) << k_lineinfo << "select(): " << perror << "\n";
507  return -1;
508  }
509  ret = 0;
510  }
511 
512  if (ret)
513  {
514  TQCString output = readAll(false);
515  bool lineStart = true;
516  while (!output.isNull())
517  {
518  if (!m_Exit.isEmpty())
519  {
520  // match exit string only at line starts
521  int pos = output.find(m_Exit.data());
522  if ((pos >= 0) && ((pos == 0 && lineStart) || (output.at (pos - 1) == '\n')))
523  {
524  kill(m_Pid, SIGTERM);
525  }
526  }
527  if (m_bTerminal)
528  {
529  fputs(output, stdout);
530  fflush(stdout);
531  }
532  lineStart = output.at( output.length() - 1 ) == '\n';
533  output = readAll(false);
534  }
535  }
536 
537  ret = checkPidExited(m_Pid);
538  if (ret == Error)
539  {
540  if (errno == ECHILD) retval = 0;
541  else retval = 1;
542  break;
543  }
544  else if (ret == Killed)
545  {
546  retval = 0;
547  break;
548  }
549  else if (ret == NotExited)
550  {
551  // keep checking
552  }
553  else
554  {
555  retval = ret;
556  break;
557  }
558  }
559  return retval;
560 }
561 
562 /*
563  * SetupTTY: Creates a new session. The filedescriptor "fd" should be
564  * connected to the tty. It is closed after the tty is reopened to make it
565  * our controlling terminal. This way the tty is always opened at least once
566  * so we'll never get EIO when reading from it.
567  */
568 
569 int PtyProcess::SetupTTY(int fd)
570 {
571  // Reset signal handlers
572  for (int sig = 1; sig < NSIG; sig++)
573  signal(sig, SIG_DFL);
574  signal(SIGHUP, SIG_IGN);
575 
576  // Close all file handles
577  struct rlimit rlp;
578  getrlimit(RLIMIT_NOFILE, &rlp);
579  for (int i = 0; i < (int)rlp.rlim_cur; i++)
580  if (i != fd) close(i);
581 
582  // Create a new session.
583  setsid();
584 
585  // Open slave. This will make it our controlling terminal
586  int slave = open(m_TTY, O_RDWR);
587  if (slave < 0)
588  {
589  kdError(900) << k_lineinfo << "Could not open slave side: " << perror << "\n";
590  return -1;
591  }
592  close(fd);
593 
594 #if defined(__SVR4) && defined(sun)
595 
596  // Solaris STREAMS environment.
597  // Push these modules to make the stream look like a terminal.
598  ioctl(slave, I_PUSH, "ptem");
599  ioctl(slave, I_PUSH, "ldterm");
600 
601 #endif
602 
603 #ifdef TIOCSCTTY
604  ioctl(slave, TIOCSCTTY, NULL);
605 #endif
606 
607  // Connect stdin, stdout and stderr
608  dup2(slave, 0); dup2(slave, 1); dup2(slave, 2);
609  if (slave > 2)
610  close(slave);
611 
612  // Disable OPOST processing. Otherwise, '\n' are (on Linux at least)
613  // translated to '\r\n'.
614  struct termios tio;
615  if (tcgetattr(0, &tio) < 0)
616  {
617  kdError(900) << k_lineinfo << "tcgetattr(): " << perror << "\n";
618  return -1;
619  }
620  tio.c_oflag &= ~OPOST;
621  if (tcsetattr(0, TCSANOW, &tio) < 0)
622  {
623  kdError(900) << k_lineinfo << "tcsetattr(): " << perror << "\n";
624  return -1;
625  }
626 
627  return 0;
628 }
629 
630 void PtyProcess::virtual_hook( int, void* )
631 { /*BASE::virtual_hook( id, data );*/ }
PTY::grantpt
int grantpt()
Grant access to the slave side.
Definition: tdesu_pty.cpp:199
PTY::getpt
int getpt()
Allocate a pty.
Definition: tdesu_pty.cpp:94
PTY
PTY compatibility routines.
Definition: tdesu_pty.h:25
PtyProcess::writeLine
void writeLine(const TQCString &line, bool addNewline=true)
Writes a line of text to the program's standard in.
Definition: process.cpp:302
PtyProcess::waitForChild
int waitForChild()
Waits for the child to exit.
Definition: process.cpp:486
PtyProcess::WaitSlave
int WaitSlave()
Waits until the pty has cleared the ECHO flag.
Definition: process.cpp:412
PtyProcess::setEnvironment
void setEnvironment(const QCStringList &env)
Set additinal environment variables.
Definition: process.cpp:159
PtyProcess::exec
int exec(const TQCString &command, const QCStringList &args)
Forks off and execute a command.
Definition: process.cpp:324
PtyProcess::unreadLine
void unreadLine(const TQCString &line, bool addNewline=true)
Puts back a line of input.
Definition: process.cpp:311
PtyProcess::enableLocalEcho
int enableLocalEcho(bool enable=true)
Enables/disables local echo on the pseudo tty.
Definition: process.cpp:450
PtyProcess::readLine
TQCString readLine(bool block=true)
Reads a line from the program's standard out.
Definition: process.cpp:175
PtyProcess::checkPid
static bool checkPid(pid_t pid)
Basic check for the existence of pid.
Definition: process.cpp:75
PTY::ptsname
TQCString ptsname()
Get the slave name.
Definition: tdesu_pty.cpp:299
PtyProcess::readAll
TQCString readAll(bool block=true)
Read all available output from the program's standard out.
Definition: process.cpp:247
PtyProcess::waitMS
static int waitMS(int fd, int ms)
Wait ms miliseconds (ie.
Definition: process.cpp:59
PTY::unlockpt
int unlockpt()
Unlock the slave side.
Definition: tdesu_pty.cpp:268

tdesu

Skip menu "tdesu"
  • Main Page
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Class Members
  • Related Pages

tdesu

Skip menu "tdesu"
  • arts
  • dcop
  • dnssd
  • interfaces
  •   kspeech
  •     interface
  •     library
  •   tdetexteditor
  • kate
  • kded
  • kdoctools
  • kimgio
  • kjs
  • libtdemid
  • libtdescreensaver
  •     tdecore
  • tdeabc
  • tdecmshell
  • tdecore
  • tdefx
  • tdehtml
  • tdeinit
  • tdeio
  •   bookmarks
  •   httpfilter
  •   kpasswdserver
  •   kssl
  • tdeioslave
  •   http
  •   tdefile
  •   tdeio
  •   tdeioexec
  • tdemdi
  •   tdemdi
  • tdenewstuff
  • tdeparts
  • tdeprint
  • tderandr
  • tderesources
  • tdespell2
  • tdesu
  • tdeui
  • tdeunittest
  • tdeutils
  • tdewallet
Generated for tdesu by doxygen 1.8.8
This website is maintained by Timothy Pearson.