• 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  unsetenv("XDG_RUNTIME_DIR");
363 
364  // set temporarily LC_ALL to C, for su (to be able to parse "Password:")
365  const char* old_lc_all = getenv( "LC_ALL" );
366  if( old_lc_all != NULL )
367  setenv( "TDESU_LC_ALL", old_lc_all, 1 );
368  else
369  unsetenv( "TDESU_LC_ALL" );
370  setenv("LC_ALL", "C", 1);
371 
372  // From now on, terminal output goes through the tty.
373 
374  TQCString path;
375  if (command.contains('/'))
376  path = command;
377  else
378  {
379  TQString file = TDEStandardDirs::findExe(command);
380  if (file.isEmpty())
381  {
382  kdError(900) << k_lineinfo << command << " not found\n";
383  _exit(1);
384  }
385  path = TQFile::encodeName(file);
386  }
387 
388  const char **argp = (const char **)malloc((args.count()+2)*sizeof(char *));
389  int i = 0;
390  argp[i++] = path;
391  for (QCStringList::ConstIterator it=args.begin(); it!=args.end(); ++it)
392  argp[i++] = *it;
393 
394  argp[i] = 0L;
395 
396  execv(path, (char * const *)argp);
397  kdError(900) << k_lineinfo << "execv(\"" << path << "\"): " << perror << "\n";
398  _exit(1);
399  return -1; // Shut up compiler. Never reached.
400 }
401 
402 
403 /*
404  * Wait until the terminal is set into no echo mode. At least one su
405  * (RH6 w/ Linux-PAM patches) sets noecho mode AFTER writing the Password:
406  * prompt, using TCSAFLUSH. This flushes the terminal I/O queues, possibly
407  * taking the password with it. So we wait until no echo mode is set
408  * before writing the password.
409  * Note that this is done on the slave fd. While Linux allows tcgetattr() on
410  * the master side, Solaris doesn't.
411  */
412 
413 int PtyProcess::WaitSlave()
414 {
415  int slave = open(m_TTY, O_RDWR);
416  if (slave < 0)
417  {
418  kdError(900) << k_lineinfo << "Could not open slave tty.\n";
419  return -1;
420  }
421 
422  kdDebug(900) << k_lineinfo << "Child pid " << m_Pid << endl;
423 
424  struct termios tio;
425  while (1)
426  {
427  if (!checkPid(m_Pid))
428  {
429  close(slave);
430  return -1;
431  }
432  if (tcgetattr(slave, &tio) < 0)
433  {
434  kdError(900) << k_lineinfo << "tcgetattr(): " << perror << "\n";
435  close(slave);
436  return -1;
437  }
438  if (tio.c_lflag & ECHO)
439  {
440  kdDebug(900) << k_lineinfo << "Echo mode still on.\n";
441  waitMS(slave,100);
442  continue;
443  }
444  break;
445  }
446  close(slave);
447  return 0;
448 }
449 
450 
451 int PtyProcess::enableLocalEcho(bool enable)
452 {
453  int slave = open(m_TTY, O_RDWR);
454  if (slave < 0)
455  {
456  kdError(900) << k_lineinfo << "Could not open slave tty.\n";
457  return -1;
458  }
459  struct termios tio;
460  if (tcgetattr(slave, &tio) < 0)
461  {
462  kdError(900) << k_lineinfo << "tcgetattr(): " << perror << "\n";
463  close(slave); return -1;
464  }
465  if (enable)
466  tio.c_lflag |= ECHO;
467  else
468  tio.c_lflag &= ~ECHO;
469  if (tcsetattr(slave, TCSANOW, &tio) < 0)
470  {
471  kdError(900) << k_lineinfo << "tcsetattr(): " << perror << "\n";
472  close(slave); return -1;
473  }
474  close(slave);
475  return 0;
476 }
477 
478 
479 /*
480  * Copy output to stdout until the child process exists, or a line of output
481  * matches `m_Exit'.
482  * We have to use waitpid() to test for exit. Merely waiting for EOF on the
483  * pty does not work, because the target process may have children still
484  * attached to the terminal.
485  */
486 
487 int PtyProcess::waitForChild()
488 {
489  int retval = 1;
490 
491  fd_set fds;
492  FD_ZERO(&fds);
493 
494  while (1)
495  {
496  int ret = 0;
497 
498  if (m_Fd != -1)
499  {
500  FD_SET(m_Fd, &fds);
501  ret = select(m_Fd+1, &fds, 0L, 0L, 0L);
502  }
503  if (ret == -1)
504  {
505  if (errno != EINTR)
506  {
507  kdError(900) << k_lineinfo << "select(): " << perror << "\n";
508  return -1;
509  }
510  ret = 0;
511  }
512 
513  if (ret)
514  {
515  TQCString output = readAll(false);
516  bool lineStart = true;
517  while (!output.isNull())
518  {
519  if (!m_Exit.isEmpty())
520  {
521  // match exit string only at line starts
522  int pos = output.find(m_Exit.data());
523  if ((pos >= 0) && ((pos == 0 && lineStart) || (output.at (pos - 1) == '\n')))
524  {
525  kill(m_Pid, SIGTERM);
526  }
527  }
528  if (m_bTerminal)
529  {
530  fputs(output, stdout);
531  fflush(stdout);
532  }
533  lineStart = output.at( output.length() - 1 ) == '\n';
534  output = readAll(false);
535  }
536  }
537 
538  ret = checkPidExited(m_Pid);
539  if (ret == Error)
540  {
541  if (errno == ECHILD) retval = 0;
542  else retval = 1;
543  break;
544  }
545  else if (ret == Killed)
546  {
547  retval = 0;
548  break;
549  }
550  else if (ret == NotExited)
551  {
552  // keep checking
553  }
554  else
555  {
556  retval = ret;
557  break;
558  }
559  }
560  return retval;
561 }
562 
563 /*
564  * SetupTTY: Creates a new session. The filedescriptor "fd" should be
565  * connected to the tty. It is closed after the tty is reopened to make it
566  * our controlling terminal. This way the tty is always opened at least once
567  * so we'll never get EIO when reading from it.
568  */
569 
570 int PtyProcess::SetupTTY(int fd)
571 {
572  // Reset signal handlers
573  for (int sig = 1; sig < NSIG; sig++)
574  signal(sig, SIG_DFL);
575  signal(SIGHUP, SIG_IGN);
576 
577  // Close all file handles
578  struct rlimit rlp;
579  getrlimit(RLIMIT_NOFILE, &rlp);
580  for (int i = 0; i < (int)rlp.rlim_cur; i++)
581  if (i != fd) close(i);
582 
583  // Create a new session.
584  setsid();
585 
586  // Open slave. This will make it our controlling terminal
587  int slave = open(m_TTY, O_RDWR);
588  if (slave < 0)
589  {
590  kdError(900) << k_lineinfo << "Could not open slave side: " << perror << "\n";
591  return -1;
592  }
593  close(fd);
594 
595 #if defined(__SVR4) && defined(sun)
596 
597  // Solaris STREAMS environment.
598  // Push these modules to make the stream look like a terminal.
599  ioctl(slave, I_PUSH, "ptem");
600  ioctl(slave, I_PUSH, "ldterm");
601 
602 #endif
603 
604 #ifdef TIOCSCTTY
605  ioctl(slave, TIOCSCTTY, NULL);
606 #endif
607 
608  // Connect stdin, stdout and stderr
609  dup2(slave, 0); dup2(slave, 1); dup2(slave, 2);
610  if (slave > 2)
611  close(slave);
612 
613  // Disable OPOST processing. Otherwise, '\n' are (on Linux at least)
614  // translated to '\r\n'.
615  struct termios tio;
616  if (tcgetattr(0, &tio) < 0)
617  {
618  kdError(900) << k_lineinfo << "tcgetattr(): " << perror << "\n";
619  return -1;
620  }
621  tio.c_oflag &= ~OPOST;
622  if (tcsetattr(0, TCSANOW, &tio) < 0)
623  {
624  kdError(900) << k_lineinfo << "tcsetattr(): " << perror << "\n";
625  return -1;
626  }
627 
628  return 0;
629 }
630 
631 void PtyProcess::virtual_hook( int, void* )
632 { /*BASE::virtual_hook( id, data );*/ }
PTY
PTY compatibility routines.
Definition: tdesu_pty.h:25
PTY::unlockpt
int unlockpt()
Unlock the slave side.
Definition: tdesu_pty.cpp:268
PTY::grantpt
int grantpt()
Grant access to the slave side.
Definition: tdesu_pty.cpp:199
PTY::ptsname
TQCString ptsname()
Get the slave name.
Definition: tdesu_pty.cpp:299
PTY::getpt
int getpt()
Allocate a pty.
Definition: tdesu_pty.cpp:94
PtyProcess::waitMS
static int waitMS(int fd, int ms)
Wait ms miliseconds (ie.
Definition: process.cpp:59
PtyProcess::fd
int fd()
Returns the filedescriptor of the process.
Definition: process.h:121
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::pid
int pid()
Returns the pid of the process.
Definition: process.h:126
PtyProcess::waitForChild
int waitForChild()
Waits for the child to exit.
Definition: process.cpp:487
PtyProcess::checkPid
static bool checkPid(pid_t pid)
Basic check for the existence of pid.
Definition: process.cpp:75
PtyProcess::readLine
TQCString readLine(bool block=true)
Reads a line from the program's standard out.
Definition: process.cpp:175
PtyProcess::readAll
TQCString readAll(bool block=true)
Read all available output from the program's standard out.
Definition: process.cpp:247
PtyProcess::enableLocalEcho
int enableLocalEcho(bool enable=true)
Enables/disables local echo on the pseudo tty.
Definition: process.cpp:451
PtyProcess::setEnvironment
void setEnvironment(const QCStringList &env)
Set additinal environment variables.
Definition: process.cpp:159
PtyProcess::WaitSlave
int WaitSlave()
Waits until the pty has cleared the ECHO flag.
Definition: process.cpp:413
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

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
  • tdeabc
  • tdecmshell
  • tdecore
  • tdefx
  • tdehtml
  • tdeinit
  • tdeio
  •   bookmarks
  •   httpfilter
  •   kpasswdserver
  •   kssl
  •   tdefile
  •   tdeio
  •   tdeioexec
  • tdeioslave
  •   http
  • tdemdi
  •   tdemdi
  • tdenewstuff
  • tdeparts
  • tdeprint
  • tderandr
  • tderesources
  • tdespell2
  • tdesu
  • tdeui
  • tdeunittest
  • tdeutils
  • tdewallet
Generated for tdesu by doxygen 1.9.1
This website is maintained by Timothy Pearson.