comparison gui/src/terminal/ProcessInfo.cpp @ 13501:86d6c3b90ad7

Added new gui files.
author Jacob Dawid <jacob.dawid@googlemail.com>
date Sun, 17 Jul 2011 17:45:05 +0200
parents
children c70511cf64ee
comparison
equal deleted inserted replaced
13500:40bd465b6c79 13501:86d6c3b90ad7
1 /*
2 Copyright 2007-2008 by Robert Knight <robertknight@gmail.countm>
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 02110-1301 USA.
18 */
19
20 // Own
21 #include "ProcessInfo.h"
22
23 // Unix
24 #include <sys/socket.h>
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
27 #include <unistd.h>
28 #include <pwd.h>
29
30 // Qt
31 #include <QtCore/QDir>
32 #include <QtCore/QFileInfo>
33 #include <QtCore/QRegExp>
34 #include <QtCore/QTextStream>
35 #include <QtCore/QStringList>
36 #include <QtCore/QSet>
37
38 // KDE
39 #include "konsole_export.h"
40
41 #if defined(Q_OS_MAC)
42 #include <sys/sysctl.h>
43 #include <libproc.h>
44 #ifdef HAVE_SYS_PROC_INFO_H
45 #include <sys/proc_info.h>
46 #endif
47 #ifdef HAVE_SYS_PROC_H
48 #include <sys/proc.h>
49 #endif
50 //#include <kde_file.h>
51 #define KDE_struct_stat struct stat
52 #define KDE_stat ::stat
53 #endif
54
55 #if defined(Q_OS_FREEBSD)
56 #include <sys/sysctl.h> //krazy:exclude=includes
57 #include <sys/types.h>
58 #include <sys/user.h>
59 #include <sys/syslimits.h>
60 #include <libutil.h>
61 #endif
62
63 ProcessInfo::ProcessInfo(int pid , bool enableEnvironmentRead)
64 : _fields( ARGUMENTS | ENVIRONMENT ) // arguments and environments
65 // are currently always valid,
66 // they just return an empty
67 // vector / map respectively
68 // if no arguments
69 // or environment bindings
70 // have been explicitly set
71 , _enableEnvironmentRead(enableEnvironmentRead)
72 , _pid(pid)
73 , _parentPid(0)
74 , _foregroundPid(0)
75 , _userId(0)
76 , _lastError(NoError)
77 , _userName(QString())
78 , _userHomeDir(QString())
79 {
80 }
81
82 ProcessInfo::Error ProcessInfo::error() const { return _lastError; }
83 void ProcessInfo::setError(Error error) { _lastError = error; }
84
85 void ProcessInfo::update()
86 {
87 readProcessInfo(_pid,_enableEnvironmentRead);
88 }
89
90 QString ProcessInfo::validCurrentDir() const
91 {
92 bool ok = false;
93
94 // read current dir, if an error occurs try the parent as the next
95 // best option
96 int currentPid = parentPid(&ok);
97 QString dir = currentDir(&ok);
98 while ( !ok && currentPid != 0 )
99 {
100 ProcessInfo* current = ProcessInfo::newInstance(currentPid);
101 current->update();
102 currentPid = current->parentPid(&ok);
103 dir = current->currentDir(&ok);
104 delete current;
105 }
106
107 return dir;
108 }
109
110 QString ProcessInfo::format(const QString& input) const
111 {
112 bool ok = false;
113
114 QString output(input);
115
116 // search for and replace known marker
117 output.replace("%u",userName());
118 output.replace("%n",name(&ok));
119 output.replace("%c",formatCommand(name(&ok),arguments(&ok),ShortCommandFormat));
120 output.replace("%C",formatCommand(name(&ok),arguments(&ok),LongCommandFormat));
121
122 QString dir = validCurrentDir();
123 if (output.contains("%D"))
124 {
125 QString homeDir = userHomeDir();
126 QString tempDir = dir;
127 // Change User's Home Dir w/ ~ only at the beginning
128 if (tempDir.startsWith(homeDir))
129 {
130 tempDir.remove(0, homeDir.length());
131 tempDir.prepend('~');
132 }
133 output.replace("%D", tempDir);
134 }
135 output.replace("%d", dir);
136
137 // remove any remaining %[LETTER] sequences
138 // output.replace(QRegExp("%\\w"), QString());
139
140 return output;
141 }
142
143 QString ProcessInfo::formatCommand(const QString& name,
144 const QVector<QString>& arguments,
145 CommandFormat format) const
146 {
147 Q_UNUSED(name);
148 Q_UNUSED(format);
149
150 // TODO Implement me
151 return QStringList(QList<QString>::fromVector(arguments)).join(" ");
152 }
153
154 QVector<QString> ProcessInfo::arguments(bool* ok) const
155 {
156 *ok = _fields & ARGUMENTS;
157
158 return _arguments;
159 }
160
161 QMap<QString,QString> ProcessInfo::environment(bool* ok) const
162 {
163 *ok = _fields & ENVIRONMENT;
164
165 return _environment;
166 }
167
168 bool ProcessInfo::isValid() const
169 {
170 return _fields & PROCESS_ID;
171 }
172
173 int ProcessInfo::pid(bool* ok) const
174 {
175 *ok = _fields & PROCESS_ID;
176
177 return _pid;
178 }
179
180 int ProcessInfo::parentPid(bool* ok) const
181 {
182 *ok = _fields & PARENT_PID;
183
184 return _parentPid;
185 }
186
187 int ProcessInfo::foregroundPid(bool* ok) const
188 {
189 *ok = _fields & FOREGROUND_PID;
190
191 return _foregroundPid;
192 }
193
194 QString ProcessInfo::name(bool* ok) const
195 {
196 *ok = _fields & NAME;
197
198 return _name;
199 }
200
201 int ProcessInfo::userId(bool* ok) const
202 {
203 *ok = _fields & UID;
204
205 return _userId;
206 }
207
208 QString ProcessInfo::userName() const
209 {
210 return _userName;
211 }
212
213 QString ProcessInfo::userHomeDir() const
214 {
215 return _userHomeDir;
216 }
217
218 void ProcessInfo::setPid(int pid)
219 {
220 _pid = pid;
221 _fields |= PROCESS_ID;
222 }
223
224 void ProcessInfo::setUserId(int uid)
225 {
226 _userId = uid;
227 _fields |= UID;
228 }
229
230 void ProcessInfo::setUserName(const QString& name)
231 {
232 _userName = name;
233 setUserHomeDir();
234 }
235
236 void ProcessInfo::setUserHomeDir()
237 {
238 QString usersName = userName();
239 // JPS: I don't know a good QT replacement
240 //if (!usersName.isEmpty())
241 // _userHomeDir = KUser(usersName).homeDir();
242 //else
243 _userHomeDir = QDir::homePath();
244 }
245
246 void ProcessInfo::setParentPid(int pid)
247 {
248 _parentPid = pid;
249 _fields |= PARENT_PID;
250 }
251 void ProcessInfo::setForegroundPid(int pid)
252 {
253 _foregroundPid = pid;
254 _fields |= FOREGROUND_PID;
255 }
256
257 QString ProcessInfo::currentDir(bool* ok) const
258 {
259 if (ok)
260 *ok = _fields & CURRENT_DIR;
261
262 return _currentDir;
263 }
264 void ProcessInfo::setCurrentDir(const QString& dir)
265 {
266 _fields |= CURRENT_DIR;
267 _currentDir = dir;
268 }
269
270 void ProcessInfo::setName(const QString& name)
271 {
272 _name = name;
273 _fields |= NAME;
274 }
275 void ProcessInfo::addArgument(const QString& argument)
276 {
277 _arguments << argument;
278 }
279
280 void ProcessInfo::addEnvironmentBinding(const QString& name , const QString& value)
281 {
282 _environment.insert(name,value);
283 }
284
285 void ProcessInfo::setFileError( QFile::FileError error )
286 {
287 switch ( error )
288 {
289 case PermissionsError:
290 setError( PermissionsError );
291 break;
292 case NoError:
293 setError( NoError );
294 break;
295 default:
296 setError( UnknownError );
297 }
298 }
299
300 //
301 // ProcessInfo::newInstance() is way at the bottom so it can see all of the
302 // implementations of the UnixProcessInfo abstract class.
303 //
304
305 NullProcessInfo::NullProcessInfo(int pid,bool enableEnvironmentRead)
306 : ProcessInfo(pid,enableEnvironmentRead)
307 {
308 }
309
310 bool NullProcessInfo::readProcessInfo(int /*pid*/ , bool /*enableEnvironmentRead*/)
311 {
312 return false;
313 }
314
315 void NullProcessInfo::readUserName()
316 {
317 }
318
319 UnixProcessInfo::UnixProcessInfo(int pid,bool enableEnvironmentRead)
320 : ProcessInfo(pid,enableEnvironmentRead)
321 {
322 }
323
324 bool UnixProcessInfo::readProcessInfo(int pid , bool enableEnvironmentRead)
325 {
326 bool ok = readProcInfo(pid);
327 if (ok)
328 {
329 ok |= readArguments(pid);
330 ok |= readCurrentDir(pid);
331 if ( enableEnvironmentRead )
332 {
333 ok |= readEnvironment(pid);
334 }
335 }
336 return ok;
337 }
338
339 void UnixProcessInfo::readUserName()
340 {
341 bool ok = false;
342 int uid = userId(&ok);
343 if (!ok) return;
344
345 struct passwd passwdStruct;
346 struct passwd *getpwResult;
347 char *getpwBuffer;
348 long getpwBufferSize;
349 int getpwStatus;
350
351 getpwBufferSize = sysconf(_SC_GETPW_R_SIZE_MAX);
352 if (getpwBufferSize == -1)
353 getpwBufferSize = 16384;
354
355 getpwBuffer = new char[getpwBufferSize];
356 if (getpwBuffer == NULL)
357 return;
358 getpwStatus = getpwuid_r(uid, &passwdStruct, getpwBuffer, getpwBufferSize, &getpwResult);
359 if (getpwResult != NULL)
360 setUserName(QString(passwdStruct.pw_name));
361 else
362 setUserName(QString());
363 delete [] getpwBuffer;
364 }
365
366
367 class LinuxProcessInfo : public UnixProcessInfo
368 {
369 public:
370 LinuxProcessInfo(int pid, bool env) :
371 UnixProcessInfo(pid,env)
372 {
373 }
374
375 private:
376 virtual bool readProcInfo(int pid)
377 {
378 // indicies of various fields within the process status file which
379 // contain various information about the process
380 const int PARENT_PID_FIELD = 3;
381 const int PROCESS_NAME_FIELD = 1;
382 const int GROUP_PROCESS_FIELD = 7;
383
384 QString parentPidString;
385 QString processNameString;
386 QString foregroundPidString;
387 QString uidLine;
388 QString uidString;
389 QStringList uidStrings;
390
391 // For user id read process status file ( /proc/<pid>/status )
392 // Can not use getuid() due to it does not work for 'su'
393 QFile statusInfo( QString("/proc/%1/status").arg(pid) );
394 if ( statusInfo.open(QIODevice::ReadOnly) )
395 {
396 QTextStream stream(&statusInfo);
397 QString statusLine;
398 do {
399 statusLine = stream.readLine(0);
400 if (statusLine.startsWith(QLatin1String("Uid:")))
401 uidLine = statusLine;
402 } while (!statusLine.isNull() && uidLine.isNull());
403
404 uidStrings << uidLine.split('\t', QString::SkipEmptyParts);
405 // Must be 5 entries: 'Uid: %d %d %d %d' and
406 // uid string must be less than 5 chars (uint)
407 if (uidStrings.size() == 5)
408 uidString = uidStrings[1];
409 if (uidString.size() > 5)
410 uidString.clear();
411
412 bool ok = false;
413 int uid = uidString.toInt(&ok);
414 if (ok)
415 setUserId(uid);
416 readUserName();
417 }
418 else
419 {
420 setFileError( statusInfo.error() );
421 return false;
422 }
423
424
425 // read process status file ( /proc/<pid/stat )
426 //
427 // the expected file format is a list of fields separated by spaces, using
428 // parenthesies to escape fields such as the process name which may itself contain
429 // spaces:
430 //
431 // FIELD FIELD (FIELD WITH SPACES) FIELD FIELD
432 //
433 QFile processInfo( QString("/proc/%1/stat").arg(pid) );
434 if ( processInfo.open(QIODevice::ReadOnly) )
435 {
436 QTextStream stream(&processInfo);
437 QString data = stream.readAll();
438
439 int stack = 0;
440 int field = 0;
441 int pos = 0;
442
443 while (pos < data.count())
444 {
445 QChar c = data[pos];
446
447 if ( c == '(' )
448 stack++;
449 else if ( c == ')' )
450 stack--;
451 else if ( stack == 0 && c == ' ' )
452 field++;
453 else
454 {
455 switch ( field )
456 {
457 case PARENT_PID_FIELD:
458 parentPidString.append(c);
459 break;
460 case PROCESS_NAME_FIELD:
461 processNameString.append(c);
462 break;
463 case GROUP_PROCESS_FIELD:
464 foregroundPidString.append(c);
465 break;
466 }
467 }
468
469 pos++;
470 }
471 }
472 else
473 {
474 setFileError( processInfo.error() );
475 return false;
476 }
477
478 // check that data was read successfully
479 bool ok = false;
480 int foregroundPid = foregroundPidString.toInt(&ok);
481 if (ok)
482 setForegroundPid(foregroundPid);
483
484 int parentPid = parentPidString.toInt(&ok);
485 if (ok)
486 setParentPid(parentPid);
487
488 if (!processNameString.isEmpty())
489 setName(processNameString);
490
491 // update object state
492 setPid(pid);
493
494 return ok;
495 }
496
497 virtual bool readArguments(int pid)
498 {
499 // read command-line arguments file found at /proc/<pid>/cmdline
500 // the expected format is a list of strings delimited by null characters,
501 // and ending in a double null character pair.
502
503 QFile argumentsFile( QString("/proc/%1/cmdline").arg(pid) );
504 if ( argumentsFile.open(QIODevice::ReadOnly) )
505 {
506 QTextStream stream(&argumentsFile);
507 QString data = stream.readAll();
508
509 QStringList argList = data.split( QChar('\0') );
510
511 foreach ( const QString &entry , argList )
512 {
513 if (!entry.isEmpty())
514 addArgument(entry);
515 }
516 }
517 else
518 {
519 setFileError( argumentsFile.error() );
520 }
521
522 return true;
523 }
524
525 virtual bool readCurrentDir(int pid)
526 {
527 QFileInfo info( QString("/proc/%1/cwd").arg(pid) );
528
529 const bool readable = info.isReadable();
530
531 if ( readable && info.isSymLink() )
532 {
533 setCurrentDir( info.symLinkTarget() );
534 return true;
535 }
536 else
537 {
538 if ( !readable )
539 setError( PermissionsError );
540 else
541 setError( UnknownError );
542
543 return false;
544 }
545 }
546
547 virtual bool readEnvironment(int pid)
548 {
549 // read environment bindings file found at /proc/<pid>/environ
550 // the expected format is a list of KEY=VALUE strings delimited by null
551 // characters and ending in a double null character pair.
552
553 QFile environmentFile( QString("/proc/%1/environ").arg(pid) );
554 if ( environmentFile.open(QIODevice::ReadOnly) )
555 {
556 QTextStream stream(&environmentFile);
557 QString data = stream.readAll();
558
559 QStringList bindingList = data.split( QChar('\0') );
560
561 foreach( const QString &entry , bindingList )
562 {
563 QString name;
564 QString value;
565
566 int splitPos = entry.indexOf('=');
567
568 if ( splitPos != -1 )
569 {
570 name = entry.mid(0,splitPos);
571 value = entry.mid(splitPos+1,-1);
572
573 addEnvironmentBinding(name,value);
574 }
575 }
576 }
577 else
578 {
579 setFileError( environmentFile.error() );
580 }
581
582 return true;
583 }
584 } ;
585
586 #if defined(Q_OS_FREEBSD)
587 class FreeBSDProcessInfo : public UnixProcessInfo
588 {
589 public:
590 FreeBSDProcessInfo(int pid, bool readEnvironment) :
591 UnixProcessInfo(pid, readEnvironment)
592 {
593 }
594
595 private:
596 virtual bool readProcInfo(int pid)
597 {
598 int managementInfoBase[4];
599 size_t mibLength;
600 struct kinfo_proc* kInfoProc;
601
602 managementInfoBase[0] = CTL_KERN;
603 managementInfoBase[1] = KERN_PROC;
604 managementInfoBase[2] = KERN_PROC_PID;
605 managementInfoBase[3] = pid;
606
607 if (sysctl(managementInfoBase, 4, NULL, &mibLength, NULL, 0) == -1)
608 return false;
609
610 kInfoProc = new struct kinfo_proc [mibLength];
611
612 if (sysctl(managementInfoBase, 4, kInfoProc, &mibLength, NULL, 0) == -1)
613 {
614 delete [] kInfoProc;
615 return false;
616 }
617
618 #if defined(__DragonFly__)
619 setName(kInfoProc->kp_comm);
620 setPid(kInfoProc->kp_pid);
621 setParentPid(kInfoProc->kp_ppid);
622 setForegroundPid(kInfoProc->kp_pgid);
623 setUserId(kInfoProc->kp_uid);
624 #else
625 setName(kInfoProc->ki_comm);
626 setPid(kInfoProc->ki_pid);
627 setParentPid(kInfoProc->ki_ppid);
628 setForegroundPid(kInfoProc->ki_pgid);
629 setUserId(kInfoProc->ki_uid);
630 #endif
631
632 readUserName();
633
634 delete [] kInfoProc;
635 return true;
636 }
637
638 virtual bool readArguments(int pid)
639 {
640 char args[ARG_MAX];
641 int managementInfoBase[4];
642 size_t len;
643
644 managementInfoBase[0] = CTL_KERN;
645 managementInfoBase[1] = KERN_PROC;
646 managementInfoBase[2] = KERN_PROC_PID;
647 managementInfoBase[3] = pid;
648
649 len = sizeof(args);
650 if (sysctl(managementInfoBase, 4, args, &len, NULL, 0) == -1)
651 return false;
652
653 const QStringList argumentList = QString(args).split(QChar('\0'));
654
655 for (QStringList::const_iterator it = argumentList.begin(); it != argumentList.end(); ++it)
656 {
657 addArgument(*it);
658 }
659
660 return true;
661 }
662
663 virtual bool readEnvironment(int pid)
664 {
665 // Not supported in FreeBSD?
666 return false;
667 }
668
669 virtual bool readCurrentDir(int pid)
670 {
671 #if defined(__DragonFly__)
672 char buf[PATH_MAX];
673 int managementInfoBase[4];
674 size_t len;
675
676 managementInfoBase[0] = CTL_KERN;
677 managementInfoBase[1] = KERN_PROC;
678 managementInfoBase[2] = KERN_PROC_CWD;
679 managementInfoBase[3] = pid;
680
681 len = sizeof(buf);
682 if (sysctl(managementInfoBase, 4, buf, &len, NULL, 0) == -1)
683 return false;
684
685 setCurrentDir(buf);
686
687 return true;
688 #else
689 int numrecords;
690 struct kinfo_file* info = 0;
691
692 info = kinfo_getfile(pid, &numrecords);
693
694 if (!info)
695 return false;
696
697 for (int i = 0; i < numrecords; ++i)
698 {
699 if (info[i].kf_fd == KF_FD_TYPE_CWD)
700 {
701 setCurrentDir(info[i].kf_path);
702
703 free(info);
704 return true;
705 }
706 }
707
708 free(info);
709 return false;
710 #endif
711 }
712 } ;
713 #endif
714
715 #if defined(Q_OS_MAC)
716 class MacProcessInfo : public UnixProcessInfo
717 {
718 public:
719 MacProcessInfo(int pid, bool env) :
720 UnixProcessInfo(pid, env)
721 {
722 }
723
724 private:
725 virtual bool readProcInfo(int pid)
726 {
727 int managementInfoBase[4];
728 size_t mibLength;
729 struct kinfo_proc* kInfoProc;
730 KDE_struct_stat statInfo;
731
732 // Find the tty device of 'pid' (Example: /dev/ttys001)
733 managementInfoBase[0] = CTL_KERN;
734 managementInfoBase[1] = KERN_PROC;
735 managementInfoBase[2] = KERN_PROC_PID;
736 managementInfoBase[3] = pid;
737
738 if (sysctl(managementInfoBase, 4, NULL, &mibLength, NULL, 0) == -1)
739 {
740 return false;
741 }
742 else
743 {
744 kInfoProc = new struct kinfo_proc [mibLength];
745 if (sysctl(managementInfoBase, 4, kInfoProc, &mibLength, NULL, 0) == -1)
746 {
747 delete [] kInfoProc;
748 return false;
749 }
750 else
751 {
752 QString deviceNumber = QString(devname(((&kInfoProc->kp_eproc)->e_tdev), S_IFCHR));
753 QString fullDeviceName = QString("/dev/") + deviceNumber.rightJustified(3, '0');
754 delete [] kInfoProc;
755
756 QByteArray deviceName = fullDeviceName.toLatin1();
757 const char* ttyName = deviceName.data();
758
759 if (KDE_stat(ttyName, &statInfo) != 0)
760 return false;
761
762 // Find all processes attached to ttyName
763 managementInfoBase[0] = CTL_KERN;
764 managementInfoBase[1] = KERN_PROC;
765 managementInfoBase[2] = KERN_PROC_TTY;
766 managementInfoBase[3] = statInfo.st_rdev;
767
768 mibLength = 0;
769 if (sysctl(managementInfoBase, sizeof(managementInfoBase)/sizeof(int), NULL, &mibLength, NULL, 0) == -1)
770 return false;
771
772 kInfoProc = new struct kinfo_proc [mibLength];
773 if (sysctl(managementInfoBase, sizeof(managementInfoBase)/sizeof(int), kInfoProc, &mibLength, NULL, 0) == -1)
774 return false;
775
776 // The foreground program is the first one
777 setName(QString(kInfoProc->kp_proc.p_comm));
778
779 delete [] kInfoProc;
780 }
781 }
782 return true;
783 }
784
785 virtual bool readArguments(int pid)
786 {
787 Q_UNUSED(pid);
788 return false;
789 }
790 virtual bool readCurrentDir(int pid)
791 {
792 struct proc_vnodepathinfo vpi;
793 int nb = proc_pidinfo(pid, PROC_PIDVNODEPATHINFO, 0, &vpi, sizeof(vpi));
794 if (nb == sizeof(vpi))
795 {
796 setCurrentDir(QString(vpi.pvi_cdir.vip_path));
797 return true;
798 }
799 return false;
800 }
801 virtual bool readEnvironment(int pid)
802 {
803 Q_UNUSED(pid);
804 return false;
805 }
806 } ;
807 #endif
808
809 #ifdef Q_OS_SOLARIS
810 // The procfs structure definition requires off_t to be
811 // 32 bits, which only applies if FILE_OFFSET_BITS=32.
812 // Futz around here to get it to compile regardless,
813 // although some of the structure sizes might be wrong.
814 // Fortunately, the structures we actually use don't use
815 // off_t, and we're safe.
816 #if defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS==64)
817 #undef _FILE_OFFSET_BITS
818 #endif
819 #include <procfs.h>
820 #else
821 // On non-Solaris platforms, define a fake psinfo structure
822 // so that the SolarisProcessInfo class can be compiled
823 //
824 // That avoids the trap where you change the API and
825 // don't notice it in #ifdeffed platform-specific parts
826 // of the code.
827 struct psinfo {
828 int pr_ppid;
829 int pr_pgid;
830 char* pr_fname;
831 char* pr_psargs;
832 } ;
833 static const int PRARGSZ=1;
834 #endif
835
836 class SolarisProcessInfo : public UnixProcessInfo
837 {
838 public:
839 SolarisProcessInfo(int pid, bool readEnvironment)
840 : UnixProcessInfo(pid,readEnvironment)
841 {
842 }
843 private:
844 virtual bool readProcInfo(int pid)
845 {
846 QFile psinfo( QString("/proc/%1/psinfo").arg(pid) );
847 if ( psinfo.open( QIODevice::ReadOnly ) )
848 {
849 struct psinfo info;
850 if (psinfo.read((char *)&info,sizeof(info)) != sizeof(info))
851 {
852 return false;
853 }
854
855 setParentPid(info.pr_ppid);
856 setForegroundPid(info.pr_pgid);
857 setName(info.pr_fname);
858 setPid(pid);
859
860 // Bogus, because we're treating the arguments as one single string
861 info.pr_psargs[PRARGSZ-1]=0;
862 addArgument(info.pr_psargs);
863 }
864 return true;
865 }
866
867 virtual bool readArguments(int /*pid*/)
868 {
869 // Handled in readProcInfo()
870 return false;
871 }
872
873 virtual bool readEnvironment(int /*pid*/)
874 {
875 // Not supported in Solaris
876 return false;
877 }
878
879 virtual bool readCurrentDir(int pid)
880 {
881 QFileInfo info( QString("/proc/%1/path/cwd").arg(pid) );
882 const bool readable = info.isReadable();
883
884 if ( readable && info.isSymLink() )
885 {
886 setCurrentDir( info.symLinkTarget() );
887 return true;
888 }
889 else
890 {
891 if ( !readable )
892 setError( PermissionsError );
893 else
894 setError( UnknownError );
895
896 return false;
897 }
898 }
899 } ;
900
901 SSHProcessInfo::SSHProcessInfo(const ProcessInfo& process)
902 : _process(process)
903 {
904 bool ok = false;
905
906 // check that this is a SSH process
907 const QString& name = _process.name(&ok);
908
909 if ( !ok || name != "ssh" )
910 {
911 //if ( !ok )
912 // kWarning() << "Could not read process info";
913 //else
914 // kWarning() << "Process is not a SSH process";
915
916 return;
917 }
918
919 // read arguments
920 const QVector<QString>& args = _process.arguments(&ok);
921
922 // SSH options
923 // these are taken from the SSH manual ( accessed via 'man ssh' )
924
925 // options which take no arguments
926 static const QString noOptionsArguments("1246AaCfgkMNnqsTtVvXxY");
927 // options which take one argument
928 static const QString singleOptionArguments("bcDeFiLlmOopRSw");
929
930 if ( ok )
931 {
932 // find the username, host and command arguments
933 //
934 // the username/host is assumed to be the first argument
935 // which is not an option
936 // ( ie. does not start with a dash '-' character )
937 // or an argument to a previous option.
938 //
939 // the command, if specified, is assumed to be the argument following
940 // the username and host
941 //
942 // note that we skip the argument at index 0 because that is the
943 // program name ( expected to be 'ssh' in this case )
944 for ( int i = 1 ; i < args.count() ; i++ )
945 {
946 // if this argument is an option then skip it, plus any
947 // following arguments which refer to this option
948 if ( args[i].startsWith('-') )
949 {
950 QChar argChar = ( args[i].length() > 1 ) ? args[i][1] : '\0';
951
952 if ( noOptionsArguments.contains(argChar) )
953 continue;
954 else if ( singleOptionArguments.contains(argChar) )
955 {
956 i++;
957 continue;
958 }
959 }
960
961 // check whether the host has been found yet
962 // if not, this must be the username/host argument
963 if ( _host.isEmpty() )
964 {
965 // check to see if only a hostname is specified, or whether
966 // both a username and host are specified ( in which case they
967 // are separated by an '@' character: username@host )
968
969 int separatorPosition = args[i].indexOf('@');
970 if ( separatorPosition != -1 )
971 {
972 // username and host specified
973 _user = args[i].left(separatorPosition);
974 _host = args[i].mid(separatorPosition+1);
975 }
976 else
977 {
978 // just the host specified
979 _host = args[i];
980 }
981 }
982 else
983 {
984 // host has already been found, this must be the command argument
985 _command = args[i];
986 }
987
988 }
989 }
990 else
991 {
992 //kWarning() << "Could not read arguments";
993
994 return;
995 }
996 }
997
998 QString SSHProcessInfo::userName() const
999 {
1000 return _user;
1001 }
1002 QString SSHProcessInfo::host() const
1003 {
1004 return _host;
1005 }
1006 QString SSHProcessInfo::command() const
1007 {
1008 return _command;
1009 }
1010 QString SSHProcessInfo::format(const QString& input) const
1011 {
1012 QString output(input);
1013
1014 // test whether host is an ip address
1015 // in which case 'short host' and 'full host'
1016 // markers in the input string are replaced with
1017 // the full address
1018 bool isIpAddress = false;
1019
1020 struct in_addr address;
1021 if ( inet_aton(_host.toLocal8Bit().constData(),&address) != 0 )
1022 isIpAddress = true;
1023 else
1024 isIpAddress = false;
1025
1026 // search for and replace known markers
1027 output.replace("%u",_user);
1028
1029 if ( isIpAddress )
1030 output.replace("%h",_host);
1031 else
1032 output.replace("%h",_host.left(_host.indexOf('.')));
1033
1034 output.replace("%H",_host);
1035 output.replace("%c",_command);
1036
1037 return output;
1038 }
1039
1040 ProcessInfo* ProcessInfo::newInstance(int pid,bool enableEnvironmentRead)
1041 {
1042 #ifdef Q_OS_LINUX
1043 return new LinuxProcessInfo(pid,enableEnvironmentRead);
1044 #elif defined(Q_OS_SOLARIS)
1045 return new SolarisProcessInfo(pid,enableEnvironmentRead);
1046 #elif defined(Q_OS_MAC)
1047 return new MacProcessInfo(pid,enableEnvironmentRead);
1048 #elif defined(Q_OS_FREEBSD)
1049 return new FreeBSDProcessInfo(pid,enableEnvironmentRead);
1050 #else
1051 return new NullProcessInfo(pid,enableEnvironmentRead);
1052 #endif
1053 }
1054