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