Mercurial > hg > octave-lyh
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 |