• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdelibs API Reference
  • KDE Home
  • Contact Us
 

KDEsu

  • sources
  • kde-4.12
  • kdelibs
  • kdesu
process.cpp
Go to the documentation of this file.
1 /* vi: ts=8 sts=4 sw=4
2  *
3  * This file is part of the KDE project, module kdesu.
4  * Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
5  *
6  * This file contains code from TEShell.C of the KDE konsole.
7  * Copyright (c) 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
8  *
9  * This is free software; you can use this library under the GNU Library
10  * General Public License, version 2. See the file "COPYING.LIB" for the
11  * exact licensing terms.
12  *
13  * process.cpp: Functionality to build a front end to password asking
14  * terminal programs.
15  */
16 
17 #include "process.h"
18 #include "kcookie.h"
19 
20 #include <config.h>
21 
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <signal.h>
27 #include <errno.h>
28 #include <string.h>
29 #include <termios.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 
37 #ifdef HAVE_SYS_SELECT_H
38 #include <sys/select.h> // Needed on some systems.
39 #endif
40 
41 #include <QtCore/QBool>
42 #include <QtCore/QFile>
43 
44 #include <ksharedconfig.h>
45 #include <kconfiggroup.h>
46 #include <kdebug.h>
47 #include <kstandarddirs.h>
48 #include <kde_file.h>
49 
50 extern int kdesuDebugArea();
51 
52 namespace KDESu {
53 
54 using namespace KDESuPrivate;
55 
56 /*
57 ** Wait for @p ms miliseconds
58 ** @param fd file descriptor
59 ** @param ms time to wait in miliseconds
60 ** @return
61 */
62 int PtyProcess::waitMS(int fd,int ms)
63 {
64  struct timeval tv;
65  tv.tv_sec = 0;
66  tv.tv_usec = 1000*ms;
67 
68  fd_set fds;
69  FD_ZERO(&fds);
70  FD_SET(fd,&fds);
71  return select(fd+1, &fds, 0L, 0L, &tv);
72 }
73 
74 // XXX this function is nonsense:
75 // - for our child, we could use waitpid().
76 // - the configurability at this place it *complete* braindamage
77 /*
78 ** Basic check for the existence of @p pid.
79 ** Returns true iff @p pid is an extant process.
80 */
81 bool PtyProcess::checkPid(pid_t pid)
82 {
83  KSharedConfig::Ptr config = KGlobal::config();
84  KConfigGroup cg(config, "super-user-command");
85  QString superUserCommand = cg.readEntry("super-user-command", "sudo");
86  //sudo does not accept signals from user so we except it
87  if (superUserCommand == "sudo") {
88  return true;
89  } else {
90  return kill(pid, 0) == 0;
91  }
92 }
93 
94 /*
95 ** Check process exit status for process @p pid.
96 ** On error (no child, no exit), return Error (-1).
97 ** If child @p pid has exited, return its exit status,
98 ** (which may be zero).
99 ** If child @p has not exited, return NotExited (-2).
100 */
101 
102 int PtyProcess::checkPidExited(pid_t pid)
103 {
104  int state, ret;
105  ret = waitpid(pid, &state, WNOHANG);
106 
107  if (ret < 0)
108  {
109  kError(kdesuDebugArea()) << k_lineinfo << "waitpid():" << perror;
110  return Error;
111  }
112  if (ret == pid)
113  {
114  if (WIFEXITED(state))
115  return WEXITSTATUS(state);
116  return Killed;
117  }
118 
119  return NotExited;
120 }
121 
122 
123 class PtyProcess::PtyProcessPrivate
124 {
125 public:
126  PtyProcessPrivate() : m_pPTY(0L) {}
127  ~PtyProcessPrivate()
128  {
129  delete m_pPTY;
130  }
131  QList<QByteArray> env;
132  KPty *m_pPTY;
133  QByteArray m_Inbuf;
134 };
135 
136 
137 PtyProcess::PtyProcess()
138  :d(new PtyProcessPrivate)
139 {
140  m_bTerminal = false;
141  m_bErase = false;
142 }
143 
144 
145 int PtyProcess::init()
146 {
147  delete d->m_pPTY;
148  d->m_pPTY = new KPty();
149  if (!d->m_pPTY->open())
150  {
151  kError(kdesuDebugArea()) << k_lineinfo << "Failed to open PTY.";
152  return -1;
153  }
154  d->m_Inbuf.resize(0);
155  return 0;
156 }
157 
158 
159 PtyProcess::~PtyProcess()
160 {
161  delete d;
162 }
163 
165 void PtyProcess::setEnvironment( const QList<QByteArray> &env )
166 {
167  d->env = env;
168 }
169 
170 int PtyProcess::fd() const
171 {
172  return d->m_pPTY ? d->m_pPTY->masterFd() : -1;
173 }
174 
175 int PtyProcess::pid() const
176 {
177  return m_Pid;
178 }
179 
181 QList<QByteArray> PtyProcess::environment() const
182 {
183  return d->env;
184 }
185 
186 
187 QByteArray PtyProcess::readAll(bool block)
188 {
189  QByteArray ret;
190  if (!d->m_Inbuf.isEmpty())
191  {
192  // if there is still something in the buffer, we need not block.
193  // we should still try to read any further output, from the fd, though.
194  block = false;
195  ret = d->m_Inbuf;
196  d->m_Inbuf.resize(0);
197  }
198 
199  int flags = fcntl(fd(), F_GETFL);
200  if (flags < 0)
201  {
202  kError(kdesuDebugArea()) << k_lineinfo << "fcntl(F_GETFL):" << perror;
203  return ret;
204  }
205  int oflags = flags;
206  if (block)
207  flags &= ~O_NONBLOCK;
208  else
209  flags |= O_NONBLOCK;
210 
211  if ((flags != oflags) && (fcntl(fd(), F_SETFL, flags) < 0))
212  {
213  // We get an error here when the child process has closed
214  // the file descriptor already.
215  return ret;
216  }
217 
218  while (1)
219  {
220  ret.reserve(ret.size() + 0x8000);
221  int nbytes = read(fd(), ret.data() + ret.size(), 0x8000);
222  if (nbytes == -1)
223  {
224  if (errno == EINTR)
225  continue;
226  else break;
227  }
228  if (nbytes == 0)
229  break; // nothing available / eof
230 
231  ret.resize(ret.size() + nbytes);
232  break;
233  }
234 
235  return ret;
236 }
237 
238 
239 QByteArray PtyProcess::readLine(bool block)
240 {
241  d->m_Inbuf = readAll(block);
242 
243  int pos;
244  QByteArray ret;
245  if (!d->m_Inbuf.isEmpty())
246  {
247  pos = d->m_Inbuf.indexOf('\n');
248  if (pos == -1)
249  {
250  // NOTE: this means we return something even if there in no full line!
251  ret = d->m_Inbuf;
252  d->m_Inbuf.resize(0);
253  } else
254  {
255  ret = d->m_Inbuf.left(pos);
256  d->m_Inbuf.remove(0, pos+1);
257  }
258  }
259 
260  return ret;
261 }
262 
263 
264 void PtyProcess::writeLine(const QByteArray &line, bool addnl)
265 {
266  if (!line.isEmpty())
267  write(fd(), line, line.length());
268  if (addnl)
269  write(fd(), "\n", 1);
270 }
271 
272 
273 void PtyProcess::unreadLine(const QByteArray &line, bool addnl)
274 {
275  QByteArray tmp = line;
276  if (addnl)
277  tmp += '\n';
278  if (!tmp.isEmpty())
279  d->m_Inbuf.prepend(tmp);
280 }
281 
282 void PtyProcess::setExitString(const QByteArray &exit)
283 {
284  m_Exit = exit;
285 }
286 
287 /*
288  * Fork and execute the command. This returns in the parent.
289  */
290 
291 int PtyProcess::exec(const QByteArray &command, const QList<QByteArray> &args)
292 {
293  kDebug(kdesuDebugArea()) << k_lineinfo << "Running" << command;
294  int i;
295 
296  if (init() < 0)
297  return -1;
298 
299  if ((m_Pid = fork()) == -1)
300  {
301  kError(kdesuDebugArea()) << k_lineinfo << "fork():" << perror;
302  return -1;
303  }
304 
305  // Parent
306  if (m_Pid)
307  {
308  d->m_pPTY->closeSlave();
309  return 0;
310  }
311 
312  // Child
313  if (setupTTY() < 0)
314  _exit(1);
315 
316  for (i = 0; i < d->env.count(); ++i)
317  {
318  putenv(const_cast<char *>(d->env.at(i).constData()));
319  }
320  unsetenv("KDE_FULL_SESSION");
321  // for : Qt: Session management error
322  unsetenv("SESSION_MANAGER");
323  // QMutex::lock , deadlocks without that.
324  // <thiago> you cannot connect to the user's session bus from another UID
325  unsetenv("DBUS_SESSION_BUS_ADDRESS");
326 
327  // set temporarily LC_ALL to C, for su (to be able to parse "Password:")
328  const QByteArray old_lc_all = qgetenv( "LC_ALL" );
329  if( !old_lc_all.isEmpty() )
330  qputenv( "KDESU_LC_ALL", old_lc_all );
331  else
332  unsetenv( "KDESU_LC_ALL" );
333  qputenv("LC_ALL", "C");
334 
335  // From now on, terminal output goes through the tty.
336 
337  QByteArray path;
338  if (command.contains('/'))
339  path = command;
340  else
341  {
342  QString file = KStandardDirs::findExe(command);
343  if (file.isEmpty())
344  {
345  kError(kdesuDebugArea()) << k_lineinfo << command << "not found.";
346  _exit(1);
347  }
348  path = QFile::encodeName(file);
349  }
350 
351  const char **argp = (const char **)malloc((args.count()+2)*sizeof(char *));
352 
353  i = 0;
354  argp[i++] = path;
355  for (QList<QByteArray>::ConstIterator it=args.begin(); it!=args.end(); ++it, ++i)
356  argp[i] = *it;
357 
358  argp[i] = NULL;
359 
360  execv(path, const_cast<char **>(argp));
361  kError(kdesuDebugArea()) << k_lineinfo << "execv(" << path << "):" << perror;
362  _exit(1);
363  return -1; // Shut up compiler. Never reached.
364 }
365 
366 
367 /*
368  * Wait until the terminal is set into no echo mode. At least one su
369  * (RH6 w/ Linux-PAM patches) sets noecho mode AFTER writing the Password:
370  * prompt, using TCSAFLUSH. This flushes the terminal I/O queues, possibly
371  * taking the password with it. So we wait until no echo mode is set
372  * before writing the password.
373  * Note that this is done on the slave fd. While Linux allows tcgetattr() on
374  * the master side, Solaris doesn't.
375  */
376 
377 int PtyProcess::WaitSlave()
378 {
379  kDebug(kdesuDebugArea()) << k_lineinfo << "Child pid" << m_Pid;
380 
381  struct termios tio;
382  while (1)
383  {
384  if (!checkPid(m_Pid))
385  {
386  kError(kdesuDebugArea()) << "process has exited while waiting for password.";
387  return -1;
388  }
389  if (!d->m_pPTY->tcGetAttr(&tio))
390  {
391  kError(kdesuDebugArea()) << k_lineinfo << "tcgetattr():" << perror;
392  return -1;
393  }
394  if (tio.c_lflag & ECHO)
395  {
396  kDebug(kdesuDebugArea()) << k_lineinfo << "Echo mode still on.";
397  usleep(10000);
398  continue;
399  }
400  break;
401  }
402  return 0;
403 }
404 
405 
406 int PtyProcess::enableLocalEcho(bool enable)
407 {
408  return d->m_pPTY->setEcho(enable) ? 0 : -1;
409 }
410 
411 
412 void PtyProcess::setTerminal(bool terminal)
413 {
414  m_bTerminal = terminal;
415 }
416 
417 void PtyProcess::setErase(bool erase)
418 {
419  m_bErase = erase;
420 }
421 
422 /*
423  * Copy output to stdout until the child process exits, or a line of output
424  * matches `m_Exit'.
425  * We have to use waitpid() to test for exit. Merely waiting for EOF on the
426  * pty does not work, because the target process may have children still
427  * attached to the terminal.
428  */
429 
430 int PtyProcess::waitForChild()
431 {
432  fd_set fds;
433  FD_ZERO(&fds);
434  QByteArray remainder;
435 
436  while (1)
437  {
438  FD_SET(fd(), &fds);
439 
440  // specify timeout to make sure select() does not block, even if the
441  // process is dead / non-responsive. It does not matter if we abort too
442  // early. In that case 0 is returned, and we'll try again in the next
443  // iteration. (As long as we don't consitently time out in each iteration)
444  timeval timeout;
445  timeout.tv_sec = 0;
446  timeout.tv_usec = 100000;
447  int ret = select(fd()+1, &fds, 0L, 0L, &timeout);
448  if (ret == -1)
449  {
450  if (errno != EINTR)
451  {
452  kError(kdesuDebugArea()) << k_lineinfo << "select():" << perror;
453  return -1;
454  }
455  ret = 0;
456  }
457 
458  if (ret)
459  {
460  forever {
461  QByteArray output = readAll(false);
462  if (output.isEmpty())
463  break;
464  if (m_bTerminal)
465  {
466  fwrite(output.constData(), output.size(), 1, stdout);
467  fflush(stdout);
468  }
469  if (!m_Exit.isEmpty())
470  {
471  // match exit string only at line starts
472  remainder += output;
473  while (remainder.length() >= m_Exit.length()) {
474  if (remainder.startsWith(m_Exit)) {
475  kill(m_Pid, SIGTERM);
476  remainder.remove(0, m_Exit.length());
477  }
478  int off = remainder.indexOf('\n');
479  if (off < 0)
480  break;
481  remainder.remove(0, off + 1);
482  }
483  }
484  }
485  }
486 
487  ret = checkPidExited(m_Pid);
488  if (ret == Error)
489  {
490  if (errno == ECHILD) return 0;
491  else return 1;
492  }
493  else if (ret == Killed)
494  {
495  return 0;
496  }
497  else if (ret == NotExited)
498  {
499  // keep checking
500  }
501  else
502  {
503  return ret;
504  }
505  }
506 }
507 
508 /*
509  * SetupTTY: Creates a new session. The filedescriptor "fd" should be
510  * connected to the tty. It is closed after the tty is reopened to make it
511  * our controlling terminal. This way the tty is always opened at least once
512  * so we'll never get EIO when reading from it.
513  */
514 
515 int PtyProcess::setupTTY()
516 {
517  // Reset signal handlers
518  for (int sig = 1; sig < NSIG; sig++)
519  KDE_signal(sig, SIG_DFL);
520  KDE_signal(SIGHUP, SIG_IGN);
521 
522  d->m_pPTY->setCTty();
523 
524  // Connect stdin, stdout and stderr
525  int slave = d->m_pPTY->slaveFd();
526  dup2(slave, 0); dup2(slave, 1); dup2(slave, 2);
527 
528  // Close all file handles
529  // XXX this caused problems in KProcess - not sure why anymore. -- ???
530  // Because it will close the start notification pipe. -- ossi
531  struct rlimit rlp;
532  getrlimit(RLIMIT_NOFILE, &rlp);
533  for (int i = 3; i < (int)rlp.rlim_cur; i++)
534  close(i);
535 
536  // Disable OPOST processing. Otherwise, '\n' are (on Linux at least)
537  // translated to '\r\n'.
538  struct ::termios tio;
539  if (tcgetattr(0, &tio) < 0)
540  {
541  kError(kdesuDebugArea()) << k_lineinfo << "tcgetattr():" << perror;
542  return -1;
543  }
544  tio.c_oflag &= ~OPOST;
545  if (tcsetattr(0, TCSANOW, &tio) < 0)
546  {
547  kError(kdesuDebugArea()) << k_lineinfo << "tcsetattr():" << perror;
548  return -1;
549  }
550 
551  return 0;
552 }
553 
554 void PtyProcess::virtual_hook( int, void* )
555 { /*BASE::virtual_hook( id, data );*/ }
556 
557 }
KDESu::PtyProcess::m_bTerminal
bool m_bTerminal
Indicates running in a terminal, causes additional newlines to be printed after output.
Definition: process.h:181
KSharedPtr< KSharedConfig >
kcookie.h
perror
QDebug perror(QDebug s, KDebugTag)
kdebug.h
KDESu::PtyProcess::environment
QList< QByteArray > environment() const
Returns the additional environment variables set by setEnvironment()
Definition: process.cpp:181
KDESu::PtyProcess::PtyProcess
PtyProcess()
Definition: process.cpp:137
KDESu::PtyProcess::setExitString
void setExitString(const QByteArray &exit)
Sets the exit string.
Definition: process.cpp:282
KDESu::PtyProcess::readLine
QByteArray readLine(bool block=true)
Reads a line from the program's standard out.
Definition: process.cpp:239
timeout
int timeout
kdesuDebugArea
int kdesuDebugArea()
Definition: su.cpp:43
kError
static QDebug kError(bool cond, int area=KDE_DEFAULT_DEBUG_AREA)
KDESu::PtyProcess::waitForChild
int waitForChild()
Waits for the child to exit.
Definition: process.cpp:430
KDESu::PtyProcess::pid
int pid() const
Returns the pid of the process.
Definition: process.cpp:175
QString
kDebug
static QDebug kDebug(bool cond, int area=KDE_DEFAULT_DEBUG_AREA)
config
KSharedConfigPtr config()
KDESu::PtyProcess::m_Pid
int m_Pid
PID of child process.
Definition: process.h:185
KDESu::PtyProcess::Error
No child.
Definition: process.h:163
output
void output(QList< Action > actions, QHash< QString, QString > domain)
KDESu::PtyProcess::m_bErase
bool m_bErase
Definition: process.h:181
KDESu::PtyProcess::readAll
QByteArray readAll(bool block=true)
Read all available output from the program's standard out.
Definition: process.cpp:187
Error
KDESu::PtyProcess::m_Exit
QByteArray m_Exit
String to scan for in output that indicates child has exited.
Definition: process.h:186
ksharedconfig.h
KDESu::PtyProcess::checkPid
static bool checkPid(pid_t pid)
Basic check for the existence of pid.
Definition: process.cpp:81
process.h
KDESu::PtyProcess::exec
int exec(const QByteArray &command, const QList< QByteArray > &args)
Forks off and execute a command.
Definition: process.cpp:291
KDESu::PtyProcess::setErase
void setErase(bool erase)
Overwrites the password as soon as it is used.
Definition: process.cpp:417
KConfigGroup
KDESu::PtyProcess::NotExited
Child hasn't exited.
Definition: process.h:164
KDESu::PtyProcess::WaitSlave
int WaitSlave()
Waits until the pty has cleared the ECHO flag.
Definition: process.cpp:377
KDESu::PtyProcess::writeLine
void writeLine(const QByteArray &line, bool addNewline=true)
Writes a line of text to the program's standard in.
Definition: process.cpp:264
kstandarddirs.h
KDESu::PtyProcess::unreadLine
void unreadLine(const QByteArray &line, bool addNewline=true)
Puts back a line of input.
Definition: process.cpp:273
KDESu::PtyProcess::setEnvironment
void setEnvironment(const QList< QByteArray > &env)
Set additinal environment variables.
Definition: process.cpp:165
KStandardDirs::findExe
static QString findExe(const QString &appname, const QString &pathstr=QString(), SearchOptions options=NoSearchOptions)
KDESu::PtyProcess::virtual_hook
virtual void virtual_hook(int id, void *data)
Standard hack to add virtual methods in a BC way.
Definition: process.cpp:554
KDESu::PtyProcess::Killed
Child terminated by signal.
Definition: process.h:165
KDESu::PtyProcess::checkPidExited
static int checkPidExited(pid_t pid)
Check process exit status for process pid.
Definition: process.cpp:102
KDESu::PtyProcess::fd
int fd() const
Returns the filedescriptor of the process.
Definition: process.cpp:170
k_lineinfo
#define k_lineinfo
KConfigGroup::readEntry
T readEntry(const QString &key, const T &aDefault) const
KDESu::PtyProcess::enableLocalEcho
int enableLocalEcho(bool enable=true)
Enables/disables local echo on the pseudo tty.
Definition: process.cpp:406
KDESu::PtyProcess::~PtyProcess
virtual ~PtyProcess()
Definition: process.cpp:159
kconfiggroup.h
QList< QByteArray >
KDESu::PtyProcess::setTerminal
void setTerminal(bool terminal)
Enables/disables terminal output.
Definition: process.cpp:412
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 22:49:48 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KDEsu

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

kdelibs API Reference

Skip menu "kdelibs API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  • kjsembed
  •   WTF
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Nepomuk-Core
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal