lsof.cpp 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. /*
  2. * Copyright (c) 2020, Maciej Zygmanowski <sppmacd@pm.me>
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright notice, this
  9. * list of conditions and the following disclaimer.
  10. *
  11. * 2. Redistributions in binary form must reproduce the above copyright notice,
  12. * this list of conditions and the following disclaimer in the documentation
  13. * and/or other materials provided with the distribution.
  14. *
  15. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  16. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  18. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  19. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  20. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  21. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  22. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  23. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  24. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. */
  26. #include <AK/GenericLexer.h>
  27. #include <AK/HashMap.h>
  28. #include <AK/JsonArray.h>
  29. #include <AK/JsonObject.h>
  30. #include <AK/JsonParser.h>
  31. #include <AK/JsonValue.h>
  32. #include <AK/String.h>
  33. #include <AK/Vector.h>
  34. #include <LibCore/ArgsParser.h>
  35. #include <LibCore/File.h>
  36. #include <LibCore/ProcessStatisticsReader.h>
  37. #include <ctype.h>
  38. #include <stdio.h>
  39. struct OpenFile {
  40. int fd;
  41. int pid;
  42. String type;
  43. String name;
  44. String state;
  45. String full_name;
  46. };
  47. static bool parse_name(StringView name, OpenFile& file)
  48. {
  49. GenericLexer lexer(name);
  50. auto component1 = lexer.consume_until(':');
  51. if (lexer.tell_remaining() == 0) {
  52. file.name = component1;
  53. return true;
  54. } else {
  55. file.type = component1;
  56. auto component2 = lexer.consume_while([](char c) { return isprint(c) && !isspace(c) && c != '('; });
  57. lexer.ignore_while(isspace);
  58. file.name = component2;
  59. if (lexer.tell_remaining() == 0) {
  60. return true;
  61. } else {
  62. if (!lexer.consume_specific('(')) {
  63. dbg() << "parse_name: expected (";
  64. return false;
  65. }
  66. auto component3 = lexer.consume_until(')');
  67. if (lexer.tell_remaining() != 0) {
  68. dbg() << "parse_name: expected EOF";
  69. return false;
  70. }
  71. file.state = component3;
  72. return true;
  73. }
  74. }
  75. }
  76. static Vector<OpenFile> get_open_files_by_pid(pid_t pid)
  77. {
  78. auto file = Core::File::open(String::format("/proc/%d/fds", pid), Core::IODevice::OpenMode::ReadOnly);
  79. if (file.is_error()) {
  80. printf("lsof: PID %d: %s\n", pid, file.error().characters());
  81. return Vector<OpenFile>();
  82. }
  83. auto data = file.value()->read_all();
  84. JsonParser parser(data);
  85. auto result = parser.parse();
  86. if (!result.has_value()) {
  87. ASSERT_NOT_REACHED();
  88. }
  89. Vector<OpenFile> files;
  90. result.value().as_array().for_each([pid, &files](const JsonValue& object) {
  91. OpenFile open_file;
  92. open_file.pid = pid;
  93. open_file.fd = object.as_object().get("fd").to_int();
  94. String name = object.as_object().get("absolute_path").to_string();
  95. ASSERT(parse_name(name, open_file));
  96. open_file.full_name = name;
  97. files.append(open_file);
  98. });
  99. return files;
  100. }
  101. static void display_entry(const OpenFile& file, const Core::ProcessStatistics& statistics)
  102. {
  103. printf("%-28s %4d %4d %-10s %4d %s\n", statistics.name.characters(), file.pid, statistics.pgid, statistics.username.characters(), file.fd, file.full_name.characters());
  104. }
  105. int main(int argc, char* argv[])
  106. {
  107. if (pledge("stdio rpath proc", nullptr) < 0) {
  108. perror("pledge");
  109. return 1;
  110. }
  111. if (unveil("/proc", "r") < 0) {
  112. perror("unveil /proc");
  113. return 1;
  114. }
  115. // needed by ProcessStatisticsReader::get_all()
  116. if (unveil("/etc/passwd", "r") < 0) {
  117. perror("unveil /etc/passwd");
  118. return 1;
  119. }
  120. unveil(nullptr, nullptr);
  121. bool arg_all_processes { false };
  122. int arg_fd { -1 };
  123. const char* arg_uid { nullptr };
  124. int arg_uid_int = -1;
  125. int arg_pgid { -1 };
  126. pid_t arg_pid { -1 };
  127. const char* arg_file_name { nullptr };
  128. Core::ArgsParser parser;
  129. if (argc == 1)
  130. arg_all_processes = true;
  131. else {
  132. parser.add_option(arg_pid, "Select by PID", nullptr, 'p', "pid");
  133. parser.add_option(arg_fd, "Select by file descriptor", nullptr, 'd', "fd");
  134. parser.add_option(arg_uid, "Select by login/UID", nullptr, 'u', "login/UID");
  135. parser.add_option(arg_pgid, "Select by process group ID", nullptr, 'g', "PGID");
  136. parser.add_positional_argument(arg_file_name, "File name", "file name", Core::ArgsParser::Required::No);
  137. parser.parse(argc, argv);
  138. }
  139. {
  140. // try convert UID to int
  141. auto arg = String(arg_uid).to_int();
  142. if (arg.has_value())
  143. arg_uid_int = arg.value();
  144. }
  145. printf("%-28s %4s %4s %-10s %4s %s\n", "COMMAND", "PID", "PGID", "USER", "FD", "NAME");
  146. if (arg_pid == -1) {
  147. auto processes = Core::ProcessStatisticsReader::get_all();
  148. for (auto process : processes) {
  149. if (process.key == 0)
  150. continue;
  151. auto open_files = get_open_files_by_pid(process.key);
  152. if (open_files.is_empty())
  153. continue;
  154. for (auto file : open_files) {
  155. if ((arg_all_processes)
  156. || (arg_fd != -1 && file.fd == arg_fd)
  157. || (arg_uid_int != -1 && (int)process.value.uid == arg_uid_int)
  158. || (arg_uid != nullptr && process.value.username == arg_uid)
  159. || (arg_pgid != -1 && (int)process.value.pgid == arg_pgid)
  160. || (arg_file_name != nullptr && file.name == arg_file_name))
  161. display_entry(file, process.value);
  162. }
  163. }
  164. } else {
  165. auto processes = Core::ProcessStatisticsReader::get_all();
  166. auto open_files = get_open_files_by_pid(arg_pid);
  167. if (open_files.is_empty())
  168. return 0;
  169. for (auto file : open_files) {
  170. display_entry(file, processes.get(arg_pid).value());
  171. }
  172. }
  173. return 0;
  174. }