summaryrefslogtreecommitdiffhomepage
path: root/process.cpp
blob: c504330759fb33f617278579c76ac8c9350dd756 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
#include "process.h"

#include <filesystem>
#include <fstream>
#include <string>
#include <thread>

#include <boost/algorithm/string.hpp>

#include <fmt/core.h>

#include "file.h"
#include "stringhelper.h"

namespace fs = std::filesystem;

bool Reichwein::Process::is_running(pid_t pid)
{
 // 3rd position is status for pid
 fs::path pid_file{"/proc/" + std::to_string(pid) + "/stat"};
 if (!fs::exists(pid_file))
  return false;

 std::string s{Reichwein::File::getFile(pid_file)};

 auto pos0{s.find(' ', 0)};
 pos0 = s.find(' ', pos0 + 1);
 pos0++;

 auto pos1{s.find(' ', pos0 + 1)};

 std::string state{s.substr(pos0, pos1 - pos0)};

 return state == "R" || state == "S";
}

int Reichwein::Process::number_of_threads(pid_t pid)
{
 // 20th position is the number of threads for pid
 fs::path pid_file{"/proc/" + std::to_string(pid) + "/stat"};
 if (!fs::exists(pid_file))
  return false;

 std::string s{Reichwein::File::getFile(pid_file)};

 size_t pos0{};
 for (int i = 0; i < 19; i++) {
  pos0 = s.find(' ', pos0 + 1);
  if (pos0 == std::string::npos)
   throw std::runtime_error("Bad format in /proc/" + std::to_string(pid) + "/stat");
 }

 size_t pos1{s.find(' ', pos0 + 1)};

 std::string number_of_threads_s{s.substr(pos0, pos1 - pos0)};

 return std::stoi(number_of_threads_s);
}


// tcp: tcp or tcp6
bool Reichwein::Process::tcp_is_pid_listening_on(const std::string& tcp, pid_t pid, int port)
{
 std::string filename{fmt::format("/proc/{}/net/{}", pid, tcp)};
 std::ifstream f{filename, std::ios::in};
 // e.g.:
 //   sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode
 //    0: 00000000:C799 00000000:0000 0A 00000000:00000000 00:00000000 00000000   107        0 21869 1 00000000335416a4 100 0 0 10 0
 std::string s;
 std::getline(f, s); // skip head line
 while (std::getline(f, s)) {
  boost::algorithm::trim_left(s);

  size_t pos_space1{s.find(' ')};
  if (pos_space1 == std::string::npos)
   throw std::runtime_error("Expected first space in " + filename);

  size_t pos_colon1{s.find(':', pos_space1 + 1)};
  if (pos_colon1 == std::string::npos)
   throw std::runtime_error("Expected first colon in " + filename);
  
  size_t pos_space2{s.find(' ', pos_colon1 + 1)};
  if (pos_space2 == std::string::npos)
   throw std::runtime_error("Expected second space in " + filename);

  std::string port_s{s.substr(pos_colon1 + 1, pos_space2 - (pos_colon1 + 1))};
  auto current_port{std::stoul(port_s, nullptr, 16)};
  if (current_port != port)
   continue;

  // now, we are in a line related to matching local port
  
  size_t pos_space3{s.find(' ', pos_space2 + 1)};
  if (pos_space3 == std::string::npos)
   throw std::runtime_error("Expected third space in " + filename);

  size_t pos_space4{s.find(' ', pos_space3 + 1)};
  if (pos_space4 == std::string::npos)
   throw std::runtime_error("Expected fourth space in " + filename);

  std::string state_s{s.substr(pos_space3 + 1, pos_space4 - (pos_space3 + 1))};
  if (state_s == "0A") // listening state TCP_LISTEN, from <linux_source>/include/net/tcp_states.h
   return true;
 }

 return false; // not found
}

bool Reichwein::Process::is_pid_listening_on(pid_t pid, int port)
{
 return tcp_is_pid_listening_on("tcp", pid, port) || tcp_is_pid_listening_on("tcp6", pid, port);
}

bool Reichwein::Process::unix_is_pid_listening_on(pid_t pid, const std::string& path)
{
 std::string filename{fmt::format("/proc/{}/net/unix", pid)};
 std::ifstream f{filename, std::ios::in};
 // e.g.:
 // Num       RefCount Protocol Flags    Type St Inode Path
 // 000000009ce259a6: 00000003 00000000 00000000 0001 03 29015
 // 000000003a686108: 00000003 00000000 00000000 0001 03 27263 /run/user/1000/pulse/native
 // 000000006d62b584: 00000002 00000000 00010000 0001 01 6126359 @/tmp/dbus-b8l6vAYN
 std::string s;
 std::getline(f, s); // skip head line
 while (std::getline(f, s)) {
  boost::algorithm::trim_left(s);
  std::vector<std::string> columns{Reichwein::Stringhelper::split(s, " ")};
  if (columns.size() < 8)
   continue;

  std::string current_path = columns[7];

  if (current_path[0] == '@')
   current_path = current_path.substr(1);

  if (current_path != path)
   continue;

  std::string state = columns[5];

  // I would expect "0A" here, since net/unix/af_unix.c also uses
  // TCP_LISTEN etc. as in TCP sockets /proc/{}/net/tcp.
  // But practically, I always get "01"
  if (state == "0A" || state == "01") // listening state TCP_LISTEN, from <linux_source>/include/net/tcp_states.h
   return true;
 }

 return false; // not found
}

void Reichwein::Process::wait_for_pid_listening_on(pid_t pid, int port)
{
 while (!is_pid_listening_on(pid, port)) {
  std::this_thread::sleep_for(std::chrono::milliseconds(10));
 }
}

void Reichwein::Process::wait_for_pid_listening_on(pid_t pid, const std::string& path)
{
 while (!unix_is_pid_listening_on(pid, path)) {
  std::this_thread::sleep_for(std::chrono::milliseconds(10));
 }
}