summaryrefslogtreecommitdiff
path: root/noncore/unsupported/libopie/oprocess.cpp
authormickeyl <mickeyl>2004-11-16 19:14:18 (UTC)
committer mickeyl <mickeyl>2004-11-16 19:14:18 (UTC)
commitea3945a9bd8f9830f70b1efa133f9df13b19362f (patch) (unidiff)
treef2ea22cc50e9aa8aa73ee7dea148f41c563c9666 /noncore/unsupported/libopie/oprocess.cpp
parent1c6f490e8541626f68422e0a3a7c7281d7f5b7d3 (diff)
downloadopie-ea3945a9bd8f9830f70b1efa133f9df13b19362f.zip
opie-ea3945a9bd8f9830f70b1efa133f9df13b19362f.tar.gz
opie-ea3945a9bd8f9830f70b1efa133f9df13b19362f.tar.bz2
libopie1 goes into unsupported
Diffstat (limited to 'noncore/unsupported/libopie/oprocess.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r--noncore/unsupported/libopie/oprocess.cpp925
1 files changed, 925 insertions, 0 deletions
diff --git a/noncore/unsupported/libopie/oprocess.cpp b/noncore/unsupported/libopie/oprocess.cpp
new file mode 100644
index 0000000..c19881a
--- a/dev/null
+++ b/noncore/unsupported/libopie/oprocess.cpp
@@ -0,0 +1,925 @@
1/*
2
3 $Id$
4
5 This file is part of the KDE libraries
6 Copyright (C) 1997 Christian Czezatke (e9025461@student.tuwien.ac.at)
7
8 This library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Library General Public
10 License as published by the Free Software Foundation; either
11 version 2 of the License, or (at your option) any later version.
12
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Library General Public License for more details.
17
18 You should have received a copy of the GNU Library General Public License
19 along with this library; see the file COPYING.LIB. If not, write to
20 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 Boston, MA 02111-1307, USA.
22
23*/
24
25
26//
27// KPROCESS -- A class for handling child processes in KDE without
28// having to take care of Un*x specific implementation details
29//
30// version 0.3.1, Jan 8th 1998
31//
32// (C) Christian Czezatke
33// e9025461@student.tuwien.ac.at
34//
35// Changes:
36//
37// March 2nd, 1998: Changed parameter list for KShellProcess:
38// Arguments are now placed in a single string so that
39// <shell> -c <commandstring> is passed to the shell
40// to make the use of "operator<<" consistent with KProcess
41//
42//
43// Ported by Holger Freyther
44// <zekce> Harlekin: oprocess and say it was ported to Qt by the Opie developers an Qt 2
45
46
47
48#include "oprocess.h"
49#define _MAY_INCLUDE_KPROCESSCONTROLLER_
50#include "oprocctrl.h"
51
52//#include <config.h>
53
54#include <qfile.h>
55#include <qsocketnotifier.h>
56
57#include <sys/time.h>
58#include <sys/types.h>
59#include <sys/stat.h>
60#include <sys/socket.h>
61
62#include <errno.h>
63#include <fcntl.h>
64#include <stdlib.h>
65#include <signal.h>
66#include <stdio.h>
67#include <string.h>
68#include <unistd.h>
69#ifdef HAVE_SYS_SELECT_H
70#include <sys/select.h>
71#endif
72#ifdef HAVE_INITGROUPS
73#include <grp.h>
74#endif
75#include <pwd.h>
76
77#include <qapplication.h>
78#include <qmap.h>
79//#include <kdebug.h>
80
81/////////////////////////////
82// public member functions //
83/////////////////////////////
84
85class OProcessPrivate {
86public:
87 OProcessPrivate() : useShell(false) { }
88
89 bool useShell;
90 QMap<QString,QString> env;
91 QString wd;
92 QCString shell;
93};
94
95
96OProcess::OProcess(QObject *parent, const char *name)
97 : QObject(parent, name)
98{
99 init ( );
100}
101
102OProcess::OProcess(const QString &arg0, QObject *parent, const char *name)
103 : QObject(parent, name)
104{
105 init ( );
106 *this << arg0;
107}
108
109OProcess::OProcess(const QStringList &args, QObject *parent, const char *name)
110 : QObject(parent, name)
111{
112 init ( );
113 *this << args;
114}
115
116void OProcess::init ( )
117{
118 run_mode = NotifyOnExit;
119 runs = false;
120 pid_ = 0;
121 status = 0;
122 keepPrivs = false;
123 innot = 0;
124 outnot = 0;
125 errnot = 0;
126 communication = NoCommunication;
127 input_data = 0;
128 input_sent = 0;
129 input_total = 0;
130 d = 0;
131
132 if (0 == OProcessController::theOProcessController) {
133 (void) new OProcessController();
134 CHECK_PTR(OProcessController::theOProcessController);
135 }
136
137 OProcessController::theOProcessController->addOProcess(this);
138 out[0] = out[1] = -1;
139 in[0] = in[1] = -1;
140 err[0] = err[1] = -1;
141}
142
143void
144OProcess::setEnvironment(const QString &name, const QString &value)
145{
146 if (!d)
147 d = new OProcessPrivate;
148 d->env.insert(name, value);
149}
150
151void
152OProcess::setWorkingDirectory(const QString &dir)
153{
154 if (!d)
155 d = new OProcessPrivate;
156 d->wd = dir;
157}
158
159void
160OProcess::setupEnvironment()
161{
162 if (d)
163 {
164 QMap<QString,QString>::Iterator it;
165 for(it = d->env.begin(); it != d->env.end(); ++it)
166 setenv(QFile::encodeName(it.key()).data(),
167 QFile::encodeName(it.data()).data(), 1);
168 if (!d->wd.isEmpty())
169 chdir(QFile::encodeName(d->wd).data());
170 }
171}
172
173void
174OProcess::setRunPrivileged(bool keepPrivileges)
175{
176 keepPrivs = keepPrivileges;
177}
178
179bool
180OProcess::runPrivileged() const
181{
182 return keepPrivs;
183}
184
185
186OProcess::~OProcess()
187{
188 // destroying the OProcess instance sends a SIGKILL to the
189 // child process (if it is running) after removing it from the
190 // list of valid processes (if the process is not started as
191 // "DontCare")
192
193 OProcessController::theOProcessController->removeOProcess(this);
194 // this must happen before we kill the child
195 // TODO: block the signal while removing the current process from the process list
196
197 if (runs && (run_mode != DontCare))
198 kill(SIGKILL);
199
200 // Clean up open fd's and socket notifiers.
201 closeStdin();
202 closeStdout();
203 closeStderr();
204
205 // TODO: restore SIGCHLD and SIGPIPE handler if this is the last OProcess
206 delete d;
207}
208
209void OProcess::detach()
210{
211 OProcessController::theOProcessController->removeOProcess(this);
212
213 runs = false;
214 pid_ = 0;
215
216 // Clean up open fd's and socket notifiers.
217 closeStdin();
218 closeStdout();
219 closeStderr();
220}
221
222bool OProcess::setExecutable(const QString& proc)
223{
224 if (runs) return false;
225
226 if (proc.isEmpty()) return false;
227
228 if (!arguments.isEmpty())
229 arguments.remove(arguments.begin());
230 arguments.prepend(QFile::encodeName(proc));
231
232 return true;
233}
234
235OProcess &OProcess::operator<<(const QStringList& args)
236{
237 QStringList::ConstIterator it = args.begin();
238 for ( ; it != args.end() ; ++it )
239 arguments.append(QFile::encodeName(*it));
240 return *this;
241}
242
243OProcess &OProcess::operator<<(const QCString& arg)
244{
245 return operator<< (arg.data());
246}
247
248OProcess &OProcess::operator<<(const char* arg)
249{
250 arguments.append(arg);
251 return *this;
252}
253
254OProcess &OProcess::operator<<(const QString& arg)
255{
256 arguments.append(QFile::encodeName(arg));
257 return *this;
258}
259
260void OProcess::clearArguments()
261{
262 arguments.clear();
263}
264
265bool OProcess::start(RunMode runmode, Communication comm)
266{
267 uint i;
268 uint n = arguments.count();
269 char **arglist;
270
271 if (runs || (0 == n)) {
272 return false; // cannot start a process that is already running
273 // or if no executable has been assigned
274 }
275 run_mode = runmode;
276 status = 0;
277
278 QCString shellCmd;
279 if (d && d->useShell)
280 {
281 if (d->shell.isEmpty())
282 {
283 qWarning( "Could not find a valid shell" );
284 return false;
285 }
286
287 arglist = static_cast<char **>(malloc( (4)*sizeof(char *)));
288 for (i=0; i < n; i++) {
289 shellCmd += arguments[i];
290 shellCmd += " "; // CC: to separate the arguments
291 }
292
293 arglist[0] = d->shell.data();
294 arglist[1] = (char *) "-c";
295 arglist[2] = shellCmd.data();
296 arglist[3] = 0;
297 }
298 else
299 {
300 arglist = static_cast<char **>(malloc( (n+1)*sizeof(char *)));
301 for (i=0; i < n; i++)
302 arglist[i] = arguments[i].data();
303 arglist[n]= 0;
304 }
305
306 if (!setupCommunication(comm))
307 qWarning( "Could not setup Communication!");
308
309 // We do this in the parent because if we do it in the child process
310 // gdb gets confused when the application runs from gdb.
311 uid_t uid = getuid();
312 gid_t gid = getgid();
313#ifdef HAVE_INITGROUPS
314 struct passwd *pw = getpwuid(uid);
315#endif
316
317 int fd[2];
318 if (0 > pipe(fd))
319 {
320 fd[0] = fd[1] = 0; // Pipe failed.. continue
321 }
322
323 runs = true;
324
325 QApplication::flushX();
326
327 // WABA: Note that we use fork() and not vfork() because
328 // vfork() has unclear semantics and is not standardized.
329 pid_ = fork();
330
331 if (0 == pid_) {
332 if (fd[0])
333 close(fd[0]);
334 if (!runPrivileged())
335 {
336 setgid(gid);
337#if defined( HAVE_INITGROUPS)
338 if(pw)
339 initgroups(pw->pw_name, pw->pw_gid);
340#endif
341 setuid(uid);
342 }
343 // The child process
344 if(!commSetupDoneC())
345 qWarning( "Could not finish comm setup in child!" );
346
347 setupEnvironment();
348
349 // Matthias
350 if (run_mode == DontCare)
351 setpgid(0,0);
352 // restore default SIGPIPE handler (Harri)
353 struct sigaction act;
354 sigemptyset(&(act.sa_mask));
355 sigaddset(&(act.sa_mask), SIGPIPE);
356 act.sa_handler = SIG_DFL;
357 act.sa_flags = 0;
358 sigaction(SIGPIPE, &act, 0L);
359
360 // We set the close on exec flag.
361 // Closing of fd[1] indicates that the execvp succeeded!
362 if (fd[1])
363 fcntl(fd[1], F_SETFD, FD_CLOEXEC);
364 execvp(arglist[0], arglist);
365 char resultByte = 1;
366 if (fd[1])
367 write(fd[1], &resultByte, 1);
368 _exit(-1);
369 } else if (-1 == pid_) {
370 // forking failed
371
372 runs = false;
373 free(arglist);
374 return false;
375 } else {
376 if (fd[1])
377 close(fd[1]);
378 // the parent continues here
379
380 // Discard any data for stdin that might still be there
381 input_data = 0;
382
383 // Check whether client could be started.
384 if (fd[0]) for(;;)
385 {
386 char resultByte;
387 int n = ::read(fd[0], &resultByte, 1);
388 if (n == 1)
389 {
390 // Error
391 runs = false;
392 close(fd[0]);
393 free(arglist);
394 pid_ = 0;
395 return false;
396 }
397 if (n == -1)
398 {
399 if ((errno == ECHILD) || (errno == EINTR))
400 continue; // Ignore
401 }
402 break; // success
403 }
404 if (fd[0])
405 close(fd[0]);
406
407 if (!commSetupDoneP()) // finish communication socket setup for the parent
408 qWarning( "Could not finish comm setup in parent!" );
409
410 if (run_mode == Block) {
411 commClose();
412
413 // The SIGCHLD handler of the process controller will catch
414 // the exit and set the status
415 while(runs)
416 {
417 OProcessController::theOProcessController->
418 slotDoHousekeeping(0);
419 }
420 runs = FALSE;
421 emit processExited(this);
422 }
423 }
424 free(arglist);
425 return true;
426}
427
428
429
430bool OProcess::kill(int signo)
431{
432 bool rv=false;
433
434 if (0 != pid_)
435 rv= (-1 != ::kill(pid_, signo));
436 // probably store errno somewhere...
437 return rv;
438}
439
440
441
442bool OProcess::isRunning() const
443{
444 return runs;
445}
446
447
448
449pid_t OProcess::pid() const
450{
451 return pid_;
452}
453
454
455
456bool OProcess::normalExit() const
457{
458 int _status = status;
459 return (pid_ != 0) && (!runs) && (WIFEXITED((_status)));
460}
461
462
463
464int OProcess::exitStatus() const
465{
466 int _status = status;
467 return WEXITSTATUS((_status));
468}
469
470
471
472bool OProcess::writeStdin(const char *buffer, int buflen)
473{
474 bool rv;
475
476 // if there is still data pending, writing new data
477 // to stdout is not allowed (since it could also confuse
478 // kprocess...
479 if (0 != input_data)
480 return false;
481
482 if (runs && (communication & Stdin)) {
483 input_data = buffer;
484 input_sent = 0;
485 input_total = buflen;
486 slotSendData(0);
487 innot->setEnabled(true);
488 rv = true;
489 } else
490 rv = false;
491 return rv;
492}
493
494void OProcess::flushStdin ( )
495{
496 if ( !input_data || ( input_sent == input_total ))
497 return;
498
499 int d1, d2;
500
501 do {
502 d1 = input_total - input_sent;
503 slotSendData ( 0 );
504 d2 = input_total - input_sent;
505 } while ( d2 <= d1 );
506}
507
508void OProcess::suspend()
509{
510 if ((communication & Stdout) && outnot)
511 outnot->setEnabled(false);
512}
513
514void OProcess::resume()
515{
516 if ((communication & Stdout) && outnot)
517 outnot->setEnabled(true);
518}
519
520bool OProcess::closeStdin()
521{
522 bool rv;
523
524 if (communication & Stdin) {
525 communication = (Communication) (communication & ~Stdin);
526 delete innot;
527 innot = 0;
528 close(in[1]);
529 rv = true;
530 } else
531 rv = false;
532 return rv;
533}
534
535bool OProcess::closeStdout()
536{
537 bool rv;
538
539 if (communication & Stdout) {
540 communication = (Communication) (communication & ~Stdout);
541 delete outnot;
542 outnot = 0;
543 close(out[0]);
544 rv = true;
545 } else
546 rv = false;
547 return rv;
548}
549
550bool OProcess::closeStderr()
551{
552 bool rv;
553
554 if (communication & Stderr) {
555 communication = static_cast<Communication>(communication & ~Stderr);
556 delete errnot;
557 errnot = 0;
558 close(err[0]);
559 rv = true;
560 } else
561 rv = false;
562 return rv;
563}
564
565
566/////////////////////////////
567// protected slots //
568/////////////////////////////
569
570
571
572void OProcess::slotChildOutput(int fdno)
573{
574 if (!childOutput(fdno))
575 closeStdout();
576}
577
578
579void OProcess::slotChildError(int fdno)
580{
581 if (!childError(fdno))
582 closeStderr();
583}
584
585
586void OProcess::slotSendData(int)
587{
588 if (input_sent == input_total) {
589 innot->setEnabled(false);
590 input_data = 0;
591 emit wroteStdin(this);
592 } else
593 input_sent += ::write(in[1], input_data+input_sent, input_total-input_sent);
594}
595
596
597
598//////////////////////////////
599// private member functions //
600//////////////////////////////
601
602
603
604void OProcess::processHasExited(int state)
605{
606 if (runs)
607 {
608 runs = false;
609 status = state;
610
611 commClose(); // cleanup communication sockets
612
613 // also emit a signal if the process was run Blocking
614 if (DontCare != run_mode)
615 {
616 emit processExited(this);
617 }
618 }
619}
620
621
622
623int OProcess::childOutput(int fdno)
624{
625 if (communication & NoRead) {
626 int len = -1;
627 emit receivedStdout(fdno, len);
628 errno = 0; // Make sure errno doesn't read "EAGAIN"
629 return len;
630 }
631 else
632 {
633 char buffer[1024];
634 int len;
635
636 len = ::read(fdno, buffer, 1024);
637
638 if ( 0 < len) {
639 emit receivedStdout(this, buffer, len);
640 }
641 return len;
642 }
643}
644
645
646
647int OProcess::childError(int fdno)
648{
649 char buffer[1024];
650 int len;
651
652 len = ::read(fdno, buffer, 1024);
653
654 if ( 0 < len)
655 emit receivedStderr(this, buffer, len);
656 return len;
657}
658
659
660
661int OProcess::setupCommunication(Communication comm)
662{
663 int ok;
664
665 communication = comm;
666
667 ok = 1;
668 if (comm & Stdin)
669 ok &= socketpair(AF_UNIX, SOCK_STREAM, 0, in) >= 0;
670
671 if (comm & Stdout)
672 ok &= socketpair(AF_UNIX, SOCK_STREAM, 0, out) >= 0;
673
674 if (comm & Stderr)
675 ok &= socketpair(AF_UNIX, SOCK_STREAM, 0, err) >= 0;
676
677 return ok;
678}
679
680
681
682int OProcess::commSetupDoneP()
683{
684 int ok = 1;
685
686 if (communication != NoCommunication) {
687 if (communication & Stdin)
688 close(in[0]);
689 if (communication & Stdout)
690 close(out[1]);
691 if (communication & Stderr)
692 close(err[1]);
693
694 // Don't create socket notifiers and set the sockets non-blocking if
695 // blocking is requested.
696 if (run_mode == Block) return ok;
697
698 if (communication & Stdin) {
699// ok &= (-1 != fcntl(in[1], F_SETFL, O_NONBLOCK));
700 innot = new QSocketNotifier(in[1], QSocketNotifier::Write, this);
701 CHECK_PTR(innot);
702 innot->setEnabled(false); // will be enabled when data has to be sent
703 QObject::connect(innot, SIGNAL(activated(int)),
704 this, SLOT(slotSendData(int)));
705 }
706
707 if (communication & Stdout) {
708// ok &= (-1 != fcntl(out[0], F_SETFL, O_NONBLOCK));
709 outnot = new QSocketNotifier(out[0], QSocketNotifier::Read, this);
710 CHECK_PTR(outnot);
711 QObject::connect(outnot, SIGNAL(activated(int)),
712 this, SLOT(slotChildOutput(int)));
713 if (communication & NoRead)
714 suspend();
715 }
716
717 if (communication & Stderr) {
718// ok &= (-1 != fcntl(err[0], F_SETFL, O_NONBLOCK));
719 errnot = new QSocketNotifier(err[0], QSocketNotifier::Read, this );
720 CHECK_PTR(errnot);
721 QObject::connect(errnot, SIGNAL(activated(int)),
722 this, SLOT(slotChildError(int)));
723 }
724 }
725 return ok;
726}
727
728
729
730int OProcess::commSetupDoneC()
731{
732 int ok = 1;
733 struct linger so;
734 memset(&so, 0, sizeof(so));
735
736 if (communication & Stdin)
737 close(in[1]);
738 if (communication & Stdout)
739 close(out[0]);
740 if (communication & Stderr)
741 close(err[0]);
742
743 if (communication & Stdin)
744 ok &= dup2(in[0], STDIN_FILENO) != -1;
745 else {
746 int null_fd = open( "/dev/null", O_RDONLY );
747 ok &= dup2( null_fd, STDIN_FILENO ) != -1;
748 close( null_fd );
749 }
750 if (communication & Stdout) {
751 ok &= dup2(out[1], STDOUT_FILENO) != -1;
752 ok &= !setsockopt(out[1], SOL_SOCKET, SO_LINGER, (char*)&so, sizeof(so));
753 }
754 else {
755 int null_fd = open( "/dev/null", O_WRONLY );
756 ok &= dup2( null_fd, STDOUT_FILENO ) != -1;
757 close( null_fd );
758 }
759 if (communication & Stderr) {
760 ok &= dup2(err[1], STDERR_FILENO) != -1;
761 ok &= !setsockopt(err[1], SOL_SOCKET, SO_LINGER, reinterpret_cast<char *>(&so), sizeof(so));
762 }
763 else {
764 int null_fd = open( "/dev/null", O_WRONLY );
765 ok &= dup2( null_fd, STDERR_FILENO ) != -1;
766 close( null_fd );
767 }
768 return ok;
769}
770
771
772
773void OProcess::commClose()
774{
775 if (NoCommunication != communication) {
776 bool b_in = (communication & Stdin);
777 bool b_out = (communication & Stdout);
778 bool b_err = (communication & Stderr);
779 if (b_in)
780 delete innot;
781
782 if (b_out || b_err) {
783 // If both channels are being read we need to make sure that one socket buffer
784 // doesn't fill up whilst we are waiting for data on the other (causing a deadlock).
785 // Hence we need to use select.
786
787 // Once one or other of the channels has reached EOF (or given an error) go back
788 // to the usual mechanism.
789
790 int fds_ready = 1;
791 fd_set rfds;
792
793 int max_fd = 0;
794 if (b_out) {
795 fcntl(out[0], F_SETFL, O_NONBLOCK);
796 if (out[0] > max_fd)
797 max_fd = out[0];
798 delete outnot;
799 outnot = 0;
800 }
801 if (b_err) {
802 fcntl(err[0], F_SETFL, O_NONBLOCK);
803 if (err[0] > max_fd)
804 max_fd = err[0];
805 delete errnot;
806 errnot = 0;
807 }
808
809
810 while (b_out || b_err) {
811 // * If the process is still running we block until we
812 // receive data. (p_timeout = 0, no timeout)
813 // * If the process has already exited, we only check
814 // the available data, we don't wait for more.
815 // (p_timeout = &timeout, timeout immediately)
816 struct timeval timeout;
817 timeout.tv_sec = 0;
818 timeout.tv_usec = 0;
819 struct timeval *p_timeout = runs ? 0 : &timeout;
820
821 FD_ZERO(&rfds);
822 if (b_out)
823 FD_SET(out[0], &rfds);
824
825 if (b_err)
826 FD_SET(err[0], &rfds);
827
828 fds_ready = select(max_fd+1, &rfds, 0, 0, p_timeout);
829 if (fds_ready <= 0) break;
830
831 if (b_out && FD_ISSET(out[0], &rfds)) {
832 int ret = 1;
833 while (ret > 0) ret = childOutput(out[0]);
834 if ((ret == -1 && errno != EAGAIN) || ret == 0)
835 b_out = false;
836 }
837
838 if (b_err && FD_ISSET(err[0], &rfds)) {
839 int ret = 1;
840 while (ret > 0) ret = childError(err[0]);
841 if ((ret == -1 && errno != EAGAIN) || ret == 0)
842 b_err = false;
843 }
844 }
845 }
846
847 if (b_in) {
848 communication = (Communication) (communication & ~Stdin);
849 close(in[1]);
850 }
851 if (b_out) {
852 communication = (Communication) (communication & ~Stdout);
853 close(out[0]);
854 }
855 if (b_err) {
856 communication = (Communication) (communication & ~Stderr);
857 close(err[0]);
858 }
859 }
860}
861
862void OProcess::setUseShell(bool useShell, const char *shell)
863{
864 if (!d)
865 d = new OProcessPrivate;
866 d->useShell = useShell;
867 d->shell = shell;
868 if (d->shell.isEmpty())
869 d->shell = searchShell();
870}
871
872QString OProcess::quote(const QString &arg)
873{
874 QString res = arg;
875 res.replace(QRegExp(QString::fromLatin1("\'")),
876 QString::fromLatin1("'\"'\"'"));
877 res.prepend('\'');
878 res.append('\'');
879 return res;
880}
881
882QCString OProcess::searchShell()
883{
884 QCString tmpShell = QCString(getenv("SHELL")).stripWhiteSpace();
885 if (!isExecutable(tmpShell))
886 {
887 tmpShell = "/bin/sh";
888 }
889
890 return tmpShell;
891}
892
893bool OProcess::isExecutable(const QCString &filename)
894{
895 struct stat fileinfo;
896
897 if (filename.isEmpty()) return false;
898
899 // CC: we've got a valid filename, now let's see whether we can execute that file
900
901 if (-1 == stat(filename.data(), &fileinfo)) return false;
902 // CC: return false if the file does not exist
903
904 // CC: anyway, we cannot execute directories, block/character devices, fifos or sockets
905 if ( (S_ISDIR(fileinfo.st_mode)) ||
906 (S_ISCHR(fileinfo.st_mode)) ||
907 (S_ISBLK(fileinfo.st_mode)) ||
908#ifdef S_ISSOCK
909 // CC: SYSVR4 systems don't have that macro
910 (S_ISSOCK(fileinfo.st_mode)) ||
911#endif
912 (S_ISFIFO(fileinfo.st_mode)) ||
913 (S_ISDIR(fileinfo.st_mode)) ) {
914 return false;
915 }
916
917 // CC: now check for permission to execute the file
918 if (access(filename.data(), X_OK) != 0) return false;
919
920 // CC: we've passed all the tests...
921 return true;
922}
923
924
925