• 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 <tdestandarddirs.h>
54
55#include "process.h"
56#include "tdesu_pty.h"
57#include "kcookie.h"
58
59int 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*/
75bool 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
96int 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
117class PtyProcess::PtyProcessPrivate
118{
119public:
120 QCStringList env;
121};
122
123
124PtyProcess::PtyProcess()
125{
126 m_bTerminal = false;
127 m_bErase = false;
128 m_pPTY = 0L;
129 d = new PtyProcessPrivate;
130}
131
132
133int 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
152PtyProcess::~PtyProcess()
153{
154 delete m_pPTY;
155 delete d;
156}
157
159void PtyProcess::setEnvironment( const QCStringList &env )
160{
161 d->env = env;
162}
163
164const 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
175TQCString 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
247TQCString 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
302void 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
311void 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
324int 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
413int 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
451int 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
487int 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
570int 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
631void 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.4
This website is maintained by Timothy Pearson.