The Pedigree Project  0.1
modules/system/status_server/main.cc
1 /*
2  * Copyright (c) 2008-2014, Pedigree Developers
3  *
4  * Please see the CONTRIB file in the root of the source tree for a full
5  * list of contributors.
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include "modules/Module.h"
21 #include "modules/system/network-stack/NetworkStack.h"
22 #include "modules/system/vfs/Filesystem.h"
23 #include "modules/system/vfs/VFS.h"
24 #include "pedigree/kernel/Log.h"
25 #include "pedigree/kernel/Version.h"
26 #include "pedigree/kernel/compiler.h"
27 #include "pedigree/kernel/core/SlamAllocator.h"
28 #include "pedigree/kernel/machine/Disk.h"
29 #include "pedigree/kernel/machine/Network.h"
30 #include "pedigree/kernel/process/Process.h"
31 #include "pedigree/kernel/process/Scheduler.h"
32 #include "pedigree/kernel/processor/Processor.h"
33 
37 
38 #define LISTEN_PORT 1234
39 
40 static Tree<struct netconn *, Mutex *> g_Netconns;
41 
42 static bool g_Running = false;
43 static Thread *g_pServerThread = nullptr;
44 
45 static void
46 netconnCallback(struct netconn *conn, enum netconn_evt evt, u16_t len)
47 {
48  Mutex *mutex = g_Netconns.lookup(conn);
49  if (mutex && (evt == NETCONN_EVT_RCVPLUS || evt == NETCONN_EVT_SENDPLUS ||
50  evt == NETCONN_EVT_ERROR))
51  {
52  // wake up waiter, positive event
53  mutex->release();
54  }
55 }
56 
57 static int clientThread(void *p)
58 {
59  if (!p)
60  return 0;
61 
62  struct netconn *connection = reinterpret_cast<struct netconn *>(p);
63  connection->callback = netconnCallback;
64 
65  bool stillOk = true;
66  bool requestComplete = false;
67 
68  String httpRequest;
69  String httpResponse;
70  err_t err;
71  while (!requestComplete)
72  {
73  struct netbuf *buf = nullptr;
74  if ((err = netconn_recv(connection, &buf)) != ERR_OK)
75  {
76  if (err == ERR_RST || err == ERR_CLSD)
77  {
78  WARNING("Unexpected disconnection from remote client.");
79  stillOk = false;
80  break;
81  }
82  else
83  {
84  ERROR("error in recv: " << lwip_strerr(err));
85  }
86  continue;
87  }
88 
89  do
90  {
91  void *data = nullptr;
92  u16_t len = 0;
93  netbuf_data(buf, &data, &len);
94 
95  if (stillOk && len)
96  {
97  httpRequest += String(reinterpret_cast<char *>(data), len);
98 
99  if (httpRequest.length() >= 4)
100  {
101  if (!(httpRequest.startswith("GET") ||
102  httpRequest.startswith("HEAD")))
103  {
104  // We really don't want to deal with this.
105  httpResponse =
106  "HTTP/1.1 400 Bad Request\r\nAllow: GET, "
107  "HEAD\r\nContent-Type: text/plain; "
108  "charset=utf-8\r\n\r\nThe Pedigree built-in status "
109  "server only accepts GET and HEAD requests.";
110  stillOk = false;
111  }
112  }
113 
114  if (stillOk)
115  {
116  if (StringContains(
117  static_cast<const char *>(httpRequest), "\r\n\r\n"))
118  {
119  // no more data needed, we have the full request
120  requestComplete = true;
121  }
122  }
123  }
124  } while (netbuf_next(buf) >= 0);
125 
126  netbuf_delete(buf);
127  }
128 
129  // no longer needing to RX any data
130  netconn_shutdown(connection, 1, 0);
131 
132  if (!stillOk)
133  {
134  if (httpResponse.length())
135  {
136  netconn_write(
137  connection, static_cast<const char *>(httpResponse),
138  httpResponse.length(), 0);
139  netconn_shutdown(connection, 1, 1);
140  }
141 
142  netconn_close(connection);
143  netconn_delete(connection);
144  return 0;
145  }
146 
147  // Build the response.
148  bool bHeadRequest = !httpRequest.startswith("GET");
149  bool bNotFound = false;
150 
151  // Got a heap of information now - prepare to return
152  size_t code = bNotFound ? 404 : 200;
153  NormalStaticString statusLine;
154  statusLine = "HTTP/1.1 ";
155  statusLine += code;
156  statusLine += " ";
157  statusLine += bNotFound ? "Not Found" : "OK";
158 
159  // Build up the reply.
160  String responseContent;
161  if (bNotFound)
162  {
163  responseContent += "Error 404: Page not found.";
164  }
165  else
166  {
167  responseContent += "<html><head><title>Pedigree - Live System Status "
168  "Report</title></head><body>";
169  responseContent += "<h1>Pedigree Live Status Report</h1>";
170  responseContent += "<p>This is a live status report from a running "
171  "Pedigree system.</p>";
172  responseContent += "<h3>Current Build</h3><pre>";
173 
174  {
175  HugeStaticString str;
176  str += "Pedigree - revision ";
177  str += g_pBuildRevision;
178  str += "<br />===========================<br />Built at ";
179  str += g_pBuildTime;
180  str += " by ";
181  str += g_pBuildUser;
182  str += " on ";
183  str += g_pBuildMachine;
184  str += "<br />Build flags: ";
185  str += g_pBuildFlags;
186  str += "<br />";
187  responseContent += str;
188  }
189 
190  responseContent += "</pre>";
191 
192  responseContent += "<h3>Network Interfaces</h3>";
193  responseContent +=
194  "<table border='1'><tr><th>Interface</th><th>IP "
195  "Addresses</th><th>Subnet "
196  "Mask</th><th>Gateway</th><th>Driver Name</th><th>MAC "
197  "address</th><th>Statistics</th></tr>";
198  for (size_t i = 0; i < NetworkStack::instance().getNumDevices(); i++)
199  {
202  StationInfo info = card->getStationInfo();
203 
204  struct netif *iface = NetworkStack::instance().getInterface(card);
205  if (!iface)
206  {
207  continue;
208  }
209 
210  // Interface number
211  responseContent += "<tr><td>";
213  s.append(iface->name, 2);
214  s.append(iface->num);
215  responseContent += s;
216  if (iface == netif_default)
217  {
218  responseContent += " <b>(default interface)</b>";
219  }
220  responseContent += "</td>";
221 
222  // IP address(es)
223  responseContent += "<td>";
224  const ip4_addr_t *ip4 = netif_ip4_addr(iface);
225  responseContent += ip4addr_ntoa(ip4);
226  for (size_t j = 0; j < LWIP_IPV6_NUM_ADDRESSES; ++j)
227  {
228  const ip6_addr_t *ip6 = netif_ip6_addr(iface, j);
229  if (ip6_addr_isany(ip6))
230  {
231  continue;
232  }
233  responseContent += "<br />";
234  responseContent += ip6addr_ntoa(ip6);
235  }
236  responseContent += "</td>";
237 
238  const ip4_addr_t *subnet4 = netif_ip4_netmask(iface);
239  const ip4_addr_t *gw4 = netif_ip4_gw(iface);
240 
241  // Subnet mask
242  responseContent += "<td>";
243  responseContent += ip4addr_ntoa(subnet4);
244  responseContent += "</td>";
245 
246  // Gateway
247  responseContent += "<td>";
248  responseContent += ip4addr_ntoa(gw4);
249  responseContent += "</td>";
250 
251  // Driver name
252  responseContent += "<td>";
253  String cardName;
254  card->getName(cardName);
255  responseContent += cardName;
256  responseContent += "</td>";
257 
258  // MAC
259  responseContent += "<td>";
260  responseContent += info.mac.toString();
261  responseContent += "</td>";
262 
263  // Statistics
264  responseContent += "<td>";
265  s.clear();
266  s += "Packets: ";
267  s.append(info.nPackets);
268  s += "<br />Dropped: ";
269  s.append(info.nDropped);
270  s += "<br />RX Errors: ";
271  s.append(info.nBad);
272  responseContent += s;
273  responseContent += "</td>";
274 
275  responseContent += "</tr>";
276  }
277  responseContent += "</table>";
278 
279  responseContent += "<h3>VFS</h3>";
280  responseContent +=
281  "<table border='1'><tr><th>VFS Alias</th><th>Disk</th></tr>";
282 
283  typedef List<String *> StringList;
284  typedef Tree<Filesystem *, List<String *> *> VFSMountTree;
285 
286  VFSMountTree &mounts = VFS::instance().getMounts();
287 
288  for (VFSMountTree::Iterator it = mounts.begin(); it != mounts.end();
289  it++)
290  {
291  Filesystem *pFs = it.key();
292  StringList *pList = it.value();
293  Disk *pDisk = pFs->getDisk();
294 
295  for (StringList::Iterator j = pList->begin(); j != pList->end();
296  j++)
297  {
298  String mount = **j;
299  String diskInfo, temp;
300 
301  if (pDisk)
302  {
303  pDisk->getName(temp);
304  pDisk->getParent()->getName(diskInfo);
305 
306  diskInfo += " -- ";
307  diskInfo += temp;
308  }
309  else
310  diskInfo = "(no disk)";
311 
312  responseContent += "<tr><td>";
313  responseContent += mount;
314  responseContent += "</td><td>";
315  responseContent += diskInfo;
316  responseContent += "</td></tr>";
317  }
318  }
319 
320  responseContent += "</table>";
321 
322 #ifdef X86_COMMON
323  responseContent += "<h3>Memory Usage (KiB)</h3>";
324  responseContent += "<table "
325  "border='1'><tr><th>Heap</th><th>Used</th><th>Free</"
326  "th></tr>";
327  {
328  extern size_t g_FreePages;
329  extern size_t g_AllocedPages;
330 
331  NormalStaticString str;
332  str += "<tr><td>";
333  str += SlamAllocator::instance().heapPageCount() * 4;
334  str += "</td><td>";
335  str += (g_AllocedPages * 4096) / 1024;
336  str += "</td><td>";
337  str += (g_FreePages * 4096) / 1024;
338  str += "</td></tr>";
339  responseContent += str;
340  }
341  responseContent += "</table>";
342 #endif
343 
344  responseContent += "<h3>Processes</h3>";
345  responseContent +=
346  "<table "
347  "border='1'><tr><th>PID</th><th>Description</"
348  "th><th>Virtual Memory (KiB)</th><th>Physical Memory "
349  "(KiB)</th><th>Shared Memory (KiB)</th>";
350  for (size_t i = 0; i < Scheduler::instance().getNumProcesses(); ++i)
351  {
352  responseContent += "<tr>";
353  Process *pProcess = Scheduler::instance().getProcess(i);
354  HugeStaticString str;
355 
356  ssize_t virtK = (pProcess->getVirtualPageCount() * 0x1000) / 1024;
357  ssize_t physK = (pProcess->getPhysicalPageCount() * 0x1000) / 1024;
358  ssize_t shrK = (pProcess->getSharedPageCount() * 0x1000) / 1024;
359 
361  str.append("<td>");
362  str.append(pProcess->getId());
363  str.append("</td><td>");
364  str.append(pProcess->description());
365  str.append("</td><td>");
366  str.append(virtK, 10);
367  str.append("</td><td>");
368  str.append(physK, 10);
369  str.append("</td><td>");
370  str.append(shrK, 10);
371  str.append("</td>");
372 
373  responseContent += str;
374  responseContent += "</tr>";
375  }
376  responseContent += "</table>";
377 
378  responseContent += "</body></html>";
379  }
380 
381  String contentLength;
382  contentLength.Format("\r\nContent-Length: %d", responseContent.length());
383 
384  httpResponse = statusLine;
385  httpResponse += contentLength;
386  httpResponse += "\r\nContent-type: text/html; charset=utf-8";
387  httpResponse += "\r\nConnection: close";
388  httpResponse += "\r\n\r\n";
389  httpResponse += responseContent;
390 
391  Mutex *mutex = new Mutex(true);
392 
393  g_Netconns.insert(connection, mutex);
394 
396  netconn_write(
397  connection, static_cast<const char *>(httpResponse),
398  httpResponse.length(), 0);
399  netconn_close(connection);
400 
401  while (!mutex->acquire())
402  ;
403 
404  g_Netconns.remove(connection);
405 
406  // Connection closed cleanly, delete our netconn now.
407  netconn_delete(connection);
408 
409  return 0;
410 }
411 
412 static int mainThread(void *)
413 {
414  struct netconn *server = netconn_new(NETCONN_TCP);
415 
416  ip_addr_t ipaddr;
417  ByteSet(&ipaddr, 0, sizeof(ipaddr));
418 
419  netconn_bind(server, &ipaddr, LISTEN_PORT);
420 
421  netconn_listen(server);
422 
423  g_Running = true;
424  while (g_Running)
425  {
427  struct netconn *connection;
428  if (netconn_accept(server, &connection) == ERR_OK)
429  {
430  Thread *pThread = new Thread(
431  Processor::information().getCurrentThread()->getParent(),
432  clientThread, connection);
433  pThread->detach();
434  }
435  }
436 
437  netconn_close(server);
438  netconn_delete(server);
439 
440  return 0;
441 }
442 
443 static bool init()
444 {
445  g_pServerThread = new Thread(
446  Processor::information().getCurrentThread()->getParent(), mainThread,
447  nullptr);
448  return true;
449 }
450 
451 static void destroy()
452 {
454  g_Running = false;
455  if (g_pServerThread)
456  {
457  g_pServerThread->join();
458  }
459 }
460 
461 MODULE_INFO("Status Server", &init, &destroy, "config", "lwip");
462 MODULE_OPTIONAL_DEPENDS("confignics");
struct netif * netif_default
Definition: netif.c:124
bool acquire(size_t n=1, size_t timeoutSecs=0, size_t timeoutUsecs=0)
Definition: Semaphore.h:62
size_t getId()
Definition: Process.h:108
virtual void getName(String &str)
Definition: Device.cc:121
Tree< Filesystem *, List< String * > * > & getMounts()
Definition: VFS.h:98
virtual const StationInfo & getStationInfo()
Definition: Network.cc:76
u8_t num
Definition: netif.h:328
Definition: Mutex.h:58
size_t nBad
Number of packets dropped by the filter.
Definition: String.h:49
static ProcessorInformation & information()
Definition: Processor.cc:45
static VFS & instance()
Definition: VFS.cc:56
Disk * getDisk()
Definition: Filesystem.h:101
Definition: Disk.h:32
Definition: err.h:113
#define WARNING(text)
Definition: Log.h:78
Network * getDevice(size_t n)
Definition: List.h:64
void insert(const K &key, const E &value)
Definition: Tree.h:173
size_t nDropped
Number of packets passed through the interface.
void release(size_t n=1)
Definition: Semaphore.cc:239
Definition: netif.h:244
A key/value dictionary.
Definition: Tree.h:33
size_t getNumProcesses()
Definition: Scheduler.cc:140
static Scheduler & instance()
Definition: Scheduler.h:48
s8_t err_t
Definition: err.h:76
Process * getProcess(size_t n)
Definition: Scheduler.cc:149
virtual void getName(String &str)
Definition: Network.cc:61
static NetworkStack & instance()
Definition: NetworkStack.h:47
Definition: Thread.h:54
Definition: err.h:111
virtual void getName(String &str)
Definition: Disk.cc:46
LargeStaticString & description()
Definition: Process.h:114
size_t getNumDevices()
#define ERROR(text)
Definition: Log.h:82
Definition: err.h:82
bool detach()
Definition: Thread.cc:885
char name[2]
Definition: netif.h:326
void remove(const K &key)
Definition: Tree.h:242
#define LWIP_IPV6_NUM_ADDRESSES
Definition: opt.h:2179
bool join()
Definition: Thread.cc:836
bool startswith(const char c) const
Definition: String.cc:748
Device * getParent() const
Definition: Device.h:149
E lookup(const K &key) const
Definition: Tree.h:192
struct netif * getInterface(Network *pCard) const
Definition: NetworkStack.h:126