summaryrefslogtreecommitdiffhomepage
path: root/process.cpp
blob: 67cf1ced436e1c1e9f2098d9e8c1200110c6ba48 (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
#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)
{
  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";
}

// 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));
 }
}