The Pedigree Project 0.1
ProcFs.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 "ProcFs.h"
21#include "pedigree/kernel/BootstrapInfo.h"
22#include "pedigree/kernel/LockGuard.h"
23#include "pedigree/kernel/Version.h"
24#include "pedigree/kernel/machine/Device.h"
25#include "pedigree/kernel/process/Scheduler.h"
26#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
27#include "pedigree/kernel/time/Time.h"
28
29#include "PosixProcess.h"
30#include "file-syscalls.h"
31#include "modules/system/users/Group.h"
32#include "modules/system/users/User.h"
33
35extern size_t g_FreePages;
36extern size_t g_AllocedPages;
37
38MeminfoFile::MeminfoFile(size_t inode, Filesystem* pParentFS, File* pParent)
39 : File(String("meminfo"), 0, 0, 0, inode, pParentFS, 0, pParent), m_Contents(), m_Lock() {
40 setPermissionsOnly(FILE_UR | FILE_GR | FILE_OR);
41 setUidOnly(0);
42 setGidOnly(0);
43
44 updateContents();
45}
46
47MeminfoFile::~MeminfoFile() = default;
48
49size_t MeminfoFile::getSize() {
50 updateContents();
51 LockGuard<Mutex> guard(m_Lock);
52 return m_Contents.length();
53}
54
55void MeminfoFile::updateContents() {
56 const auto memory = PhysicalMemoryManager::instance().memorySnapshot();
57 const uint64_t totalPages = memory.available ? memory.totalPages : g_FreePages + g_AllocedPages;
58 const uint64_t freePages = memory.available ? memory.freePages : g_FreePages;
59 const uint64_t totalKb = (totalPages * PhysicalMemoryManager::getPageSize()) / 1024;
60 const uint64_t freeKb = (freePages * PhysicalMemoryManager::getPageSize()) / 1024;
61 String contents;
62 contents.Format(
63 "MemTotal: %lu kB\n"
64 "MemFree: %lu kB\n"
65 "MemAvailable: %lu kB\n"
66 "Buffers: 0 kB\n"
67 "Cached: 0 kB\n"
68 "SwapCached: 0 kB\n"
69 "SwapTotal: 0 kB\n"
70 "SwapFree: 0 kB\n",
71 totalKb, freeKb, freeKb);
72 LockGuard<Mutex> guard(m_Lock);
73 m_Contents = contents;
74}
75
76uint64_t MeminfoFile::readBytewise(uint64_t location, uint64_t size, uintptr_t buffer,
77 bool bCanBlock) {
78 updateContents();
79 LockGuard<Mutex> guard(m_Lock);
80
81 if (location >= m_Contents.length()) {
82 return 0; // EOF
83 } else if (size > m_Contents.length() - location) {
84 size = m_Contents.length() - location;
85 }
86
87 char* destination = reinterpret_cast<char*>(buffer);
88 const char* source = static_cast<const char*>(m_Contents);
89
90 MemoryCopy(destination, source + location, size);
91
92 return size;
93}
94
95uint64_t MeminfoFile::writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer,
96 bool bCanBlock) {
97 return 0;
98}
99
100PciDevicesFile::PciDevicesFile(size_t inode, Filesystem* pParentFS, File* pParent)
101 : File(String("devices"), 0, 0, 0, inode, pParentFS, 0, pParent), m_Contents() {
102 setPermissionsOnly(FILE_UR | FILE_UW | FILE_GR | FILE_GW | FILE_OR);
103 setUidOnly(0);
104 setGidOnly(0);
105
106 resync();
107}
108
109PciDevicesFile::~PciDevicesFile() {}
110
111size_t PciDevicesFile::getSize() {
112 return m_Contents.length();
113}
114
115uint64_t PciDevicesFile::readBytewise(uint64_t location, uint64_t size, uintptr_t buffer,
116 bool bCanBlock) {
117 resync();
118
119 if (location >= m_Contents.length()) {
120 return 0; // EOF
121 } else if ((location + size) > m_Contents.length()) {
122 size = m_Contents.length() - location;
123 }
124
125 char* destination = reinterpret_cast<char*>(buffer);
126 const char* source = static_cast<const char*>(m_Contents);
127
128 StringCopy(destination, source);
129
130 return size;
131}
132
133uint64_t PciDevicesFile::writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer,
134 bool bCanBlock) {
135 return 0;
136}
137
139 m_Contents = String();
140
141 auto printer = [this](Device* p) -> Device* {
142 String initial;
143 initial.Format("%02x%02x\t%04x%04x\t%x", p->getPciBusPosition(),
144 (p->getPciDevicePosition() << 4) | p->getPciFunctionNumber(),
145 p->getPciVendorId(), p->getPciDeviceId(), p->getInterruptNumber());
146
147 String resStart;
148 String resLength;
149 for (size_t i = 0; i < 7; ++i) {
150 String thisResStart;
151 String thisResLength;
152
153 if (i < p->addresses().count()) {
154 size_t length = p->addresses()[i]->m_Size;
155
156 uintptr_t address = p->addresses()[i]->m_Address;
157 uintptr_t flags = 0;
158 if (p->addresses()[i]->m_IsIoSpace) {
159 flags = 1;
160 }
161
163 thisResStart.Format("\t%16lx", address | flags);
164 thisResLength.Format("\t%16lx", length ? length + 1 : 0);
165 } else {
166 thisResStart.Format("\t%16lx", 0);
167 thisResLength.Format("\t%16lx", 0);
168 }
169
170 resStart += thisResStart;
171 resLength += thisResLength;
172 }
173
174 m_Contents += initial;
175 m_Contents += resStart;
176 m_Contents += resLength;
177 m_Contents += "\t";
178 m_Contents += "\n";
179
180 return p;
181 };
182
183 auto callback = pedigree_std::make_callable(printer);
184 Device::foreach (callback, nullptr);
185}
186
187MountFile::MountFile(size_t inode, Filesystem* pParentFS, File* pParent)
188 : File(String("mounts"), 0, 0, 0, inode, pParentFS, 0, pParent) {
189 setPermissionsOnly(FILE_UR | FILE_GR | FILE_OR);
190 setUidOnly(0);
191 setGidOnly(0);
192}
193
194MountFile::~MountFile() = default;
195
196uint64_t MountFile::readBytewise(uint64_t location, uint64_t size, uintptr_t buffer,
197 bool bCanBlock) {
198 String mounts;
199 generate_mtab(mounts);
200
201 if (location >= mounts.length()) {
202 // "EOF"
203 return 0;
204 }
205
206 if ((location + size) >= mounts.length()) {
207 size = mounts.length() - location;
208 }
209
210 char* destination = reinterpret_cast<char*>(buffer);
211 StringCopyN(destination, static_cast<const char*>(mounts) + location, size);
212
213 return size;
214}
215
216uint64_t MountFile::writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer,
217 bool bCanBlock) {
218 return 0;
219}
220
221size_t MountFile::getSize() {
222 String mounts;
223 generate_mtab(mounts);
224 return mounts.length();
225}
226
227UptimeFile::UptimeFile(size_t inode, Filesystem* pParentFS, File* pParent)
228 : File(String("uptime"), 0, 0, 0, inode, pParentFS, 0, pParent) {
229 setPermissionsOnly(FILE_UR | FILE_GR | FILE_OR);
230 setUidOnly(0);
231 setGidOnly(0);
232}
233
234UptimeFile::~UptimeFile() = default;
235
236uint64_t UptimeFile::readBytewise(uint64_t location, uint64_t size, uintptr_t buffer,
237 bool bCanBlock) {
238 String f = generateString();
239
240 if (location >= f.length()) {
241 // "EOF"
242 return 0;
243 }
244
245 if ((location + size) >= f.length()) {
246 size = f.length() - location;
247 }
248
249 char* destination = reinterpret_cast<char*>(buffer);
250 StringCopyN(destination, static_cast<const char*>(f) + location, size);
251
252 return size;
253}
254
255uint64_t UptimeFile::writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer,
256 bool bCanBlock) {
257 return 0;
258}
259
260size_t UptimeFile::getSize() {
261 String f = generateString();
262 return f.length();
263}
264
265String UptimeFile::generateString() {
266 const uint64_t uptime = Time::getTicks();
267 const uint64_t idle = Scheduler::instance().systemActivity().idleNanoseconds;
268
269 String f;
270 f.Format("%lu.%02lu %lu.%02lu\n", uptime / Time::Multiplier::Second,
271 (uptime % Time::Multiplier::Second) / (Time::Multiplier::Second / 100),
272 idle / Time::Multiplier::Second,
273 (idle % Time::Multiplier::Second) / (Time::Multiplier::Second / 100));
274
275 return f;
276}
277
278ConstantFile::ConstantFile(String name, const char* value, size_t size, size_t inode,
279 Filesystem* pParentFS, File* pParent)
280 : File(name, 0, 0, 0, inode, pParentFS, 0, pParent), m_Contents() {
281 setPermissionsOnly(FILE_UR | FILE_UW | FILE_GR | FILE_GW | FILE_OR);
282 setUidOnly(0);
283 setGidOnly(0);
284
285 m_Contents = new char[size];
286 m_Size = size;
287 MemoryCopy(m_Contents, value, size);
288}
289
290ConstantFile::~ConstantFile() {
291 delete[] m_Contents;
292}
293
294uint64_t ConstantFile::readBytewise(uint64_t location, uint64_t size, uintptr_t buffer,
295 bool bCanBlock) {
296 if (location >= m_Size) {
297 return 0; // EOF
298 } else if ((location + size) > m_Size) {
299 size = m_Size - location;
300 }
301
302 char* destination = reinterpret_cast<char*>(buffer);
303 const char* source = m_Contents + location;
304
305 MemoryCopy(destination, source, size);
306
307 return size;
308}
309
310uint64_t ConstantFile::writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer,
311 bool bCanBlock) {
312 return 0;
313}
314
315size_t ConstantFile::getSize() {
316 return m_Size;
317}
318
319ProcFsDirectory::~ProcFsDirectory() = default;
320
321ProcFs::~ProcFs() {
322 delete m_pRoot;
323}
324
329
330 // Deterministic inode assignment to each ProcFs node
331 m_NextInode = 0;
332
333 if (m_pRoot) {
334 delete m_pRoot;
335 }
336
337 m_pRoot = new ProcFsDirectory(String(""), 0, 0, 0, getNextInode(), this, 0, 0);
338 // Allow user/group to read and write, but disallow all others anything
339 // other than the ability to list and access files.
340 m_pRoot->setPermissions(FILE_UR | FILE_UW | FILE_UX | FILE_GR | FILE_GW | FILE_GX | FILE_OR |
341 FILE_OX);
342
343 // dot entry
345 Directory* dot = new ProcFsDirectory(String("."), 0, 0, 0, m_pRoot->getInode(), this, 0, 0);
346 dot->setPermissions(m_pRoot->getPermissions());
347 m_pRoot->addEntry(dot->getName(), dot);
348
349 MeminfoFile* meminfo = new MeminfoFile(getNextInode(), this, m_pRoot);
350 m_pRoot->addEntry(meminfo->getName(), meminfo);
351
353 MountFile* mounts = new MountFile(getNextInode(), this, m_pRoot);
354 m_pRoot->addEntry(mounts->getName(), mounts);
355
356 initialiseResolverFile();
357 initialiseNetworkFile();
358
359 UptimeFile* uptime = new UptimeFile(getNextInode(), this, m_pRoot);
360 m_pRoot->addEntry(uptime->getName(), uptime);
361
362 if (!procfsAddSystemStatusFiles(*this, *m_pRoot))
363 return false;
364
365 static String fs("\text2\nnodev\tproc\nnodev\tsysfs\nnodev\ttmpfs\n");
366 ConstantFile* pFilesystems = new ConstantFile(String("filesystems"), fs.cstr(), fs.length(),
367 getNextInode(), this, m_pRoot);
368 m_pRoot->addEntry(pFilesystems->getName(), pFilesystems);
369
370 // Kernel command line
371 static String cmdline("noswap quiet boot=live\n", 25); //(g_pBootstrapInfo->getCommandLine());
372 NOTICE("cmdline is '" << cmdline << "'");
373 ConstantFile* pCmdline = new ConstantFile(String("cmdline"), cmdline.cstr(), cmdline.length(),
374 getNextInode(), this, m_pRoot);
375 m_pRoot->addEntry(pCmdline->getName(), pCmdline);
376
377 // /proc/version contains some extra version info (not same as uname)
378 static String version;
379 version.Format("Pedigree version %s (%s@%s) %s", g_pBuildRevision, g_pBuildUser, g_pBuildMachine,
380 g_pBuildTime);
381 ConstantFile* pVersion = new ConstantFile(String("version"), version.cstr(), version.length(),
382 getNextInode(), this, m_pRoot);
383 m_pRoot->addEntry(pVersion->getName(), pVersion);
384
385 ProcFsDirectory* pBusDir =
386 new ProcFsDirectory(String("bus"), 0, 0, 0, getNextInode(), this, 0, m_pRoot);
387 ProcFsDirectory* pPciDir =
388 new ProcFsDirectory(String("pci"), 0, 0, 0, getNextInode(), this, 0, pBusDir);
389
390 pBusDir->setPermissions(FILE_UR | FILE_UX | FILE_GR | FILE_GX | FILE_OR | FILE_OX);
391 pPciDir->setPermissions(FILE_UR | FILE_UX | FILE_GR | FILE_GX | FILE_OR | FILE_OX);
392
393 m_pRoot->addEntry(pBusDir->getName(), pBusDir);
394 pBusDir->addEntry(pPciDir->getName(), pPciDir);
395
396 // Load PCI devices into bus/pci/devices file
397 auto printer = [pPciDir, this](Device* p) -> Device* {
398 String bus;
399 bus.Format("%02x", p->getPciBusPosition());
400
401 // create config space file for this
402 File* d = VFS::instance().find(bus, pPciDir);
403 if (!d) {
404 ProcFsDirectory* dir = new ProcFsDirectory(bus, 0, 0, 0, getNextInode(), this, 0, pPciDir);
405 pPciDir->addEntry(dir->getName(), dir);
406
407 d = dir;
408 }
409
410 ProcFsDirectory* dir = static_cast<ProcFsDirectory*>(Directory::fromFile(d));
411
412 String fn;
413 fn.Format("%02x.%01x", p->getPciDevicePosition(), p->getPciFunctionNumber());
414
415 // Sometimes the device file already exists, avoid creating duplicate
416 // files
417 File* e = VFS::instance().find(fn, dir);
418 if (!e) {
419 PciBus::ConfigSpace space = p->getPciConfigHeader();
420
421 ConstantFile* cf = new ConstantFile(fn, reinterpret_cast<const char*>(&space), sizeof(space),
422 getNextInode(), this, dir);
423 dir->addEntry(cf->getName(), cf);
424 }
425
426 String initial;
427 initial.Format("%02x%02x\t%04x%04x\t%x", p->getPciBusPosition(),
428 (p->getPciDevicePosition() << 4) | p->getPciFunctionNumber(),
429 p->getPciVendorId(), p->getPciDeviceId(), p->getInterruptNumber());
430
431 String resStart;
432 String resLength;
433 for (size_t i = 0; i < 7; ++i) {
434 String thisResStart;
435 String thisResLength;
436
437 if (i < p->addresses().count()) {
438 size_t length = p->addresses()[i]->m_Size;
439
440 uintptr_t address = p->addresses()[i]->m_Address;
441 uintptr_t flags = 0;
442 if (p->addresses()[i]->m_IsIoSpace) {
443 flags = 1;
444 }
445
447 thisResStart.Format("\t%16lx", address | flags);
448 thisResLength.Format("\t%16lx", length ? length + 1 : 0);
449 } else {
450 thisResStart.Format("\t%16lx", 0);
451 thisResLength.Format("\t%16lx", 0);
452 }
453
454 resStart += thisResStart;
455 resLength += thisResLength;
456 }
457
458 m_PciDevices += initial;
459 m_PciDevices += resStart;
460 m_PciDevices += resLength;
461 m_PciDevices += "\t";
462 m_PciDevices += "\n";
463
464 return p;
465 };
466
467 auto callback = pedigree_std::make_callable(printer);
468 Device::foreach (callback, nullptr);
469
470 ConstantFile* pPciDevices = new ConstantFile(
471 String("devices"), m_PciDevices.cstr(), m_PciDevices.length(), getNextInode(), this, pPciDir);
472 pPciDir->addEntry(pPciDevices->getName(), pPciDevices);
473
474 return initialiseNamespaceLinks();
475}
476
477size_t ProcFs::getNextInode() {
478 return (m_NextInode += 1) - 1;
479}
480
481void ProcFs::revertInode() {
482 // Concurrent allocations may already have published later inode numbers.
483 // A failed creation leaves a gap rather than reusing a live inode.
484}
485
487 size_t pid = proc->getUserspaceId();
488
489 auto procDir = createProcessDirectory(proc);
490 if (!procDir) {
491 WARNING("ProcFs: could not prepare process directory for " << Dec << pid);
492 return;
493 }
494 procDir->setPermissions(FILE_UR | FILE_UX | FILE_GR | FILE_GX | FILE_OR | FILE_OX);
495
497 if (proc->getUser()) {
498 procDir->setUid(proc->getUser()->getId());
499 }
500 if (proc->getGroup()) {
501 procDir->setGid(proc->getGroup()->getId());
502 }
503
504 // Directory owns publication and lifetime, including concurrent exit and lookup.
505 m_pRoot->addEntry(procDir->getName(), procDir);
506
508}
509
510void ProcFs::removeProcess(PosixProcess* proc) {
511 size_t pid = proc->getUserspaceId();
512
513 String s;
514 s.Format("%d", pid);
515
516 m_pRoot->remove(s);
517}
virtual uint64_t writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
Definition ProcFs.cc:310
virtual uint64_t readBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
Definition ProcFs.cc:294
static void foreach(Callback callback, Device *root=0)
Definition Device.cc:107
static Directory * fromFile(File *pF)
Definition Directory.h:151
void remove(const HashedStringView &s)
Definition Directory.cc:597
Definition Disk.h:35
Definition File.h:75
String getName() const
Definition File.cc:782
void setGidOnly(size_t gid)
Definition File.cc:1448
void setPermissionsOnly(uint32_t perms)
Definition File.cc:1440
void setUidOnly(size_t uid)
Definition File.cc:1444
size_t getId() const
Definition Group.h:50
virtual uint64_t readBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
Definition ProcFs.cc:76
virtual uint64_t writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
Definition ProcFs.cc:95
virtual uint64_t writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
Definition ProcFs.cc:216
virtual uint64_t readBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
Definition ProcFs.cc:196
virtual uint64_t readBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
Definition ProcFs.cc:115
virtual uint64_t writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
Definition ProcFs.cc:133
void resync()
Definition ProcFs.cc:138
static PhysicalMemoryManager & instance()
void addProcess(PosixProcess *proc)
Definition ProcFs.cc:486
virtual bool initialise(Disk *pDisk)
Definition ProcFs.cc:325
size_t getUserspaceId() const
Definition Process.h:462
Group * getGroup() const
User * getUser() const
static Scheduler & instance()
Definition Scheduler.h:96
virtual uint64_t writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
Definition ProcFs.cc:255
virtual uint64_t readBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
Definition ProcFs.cc:236
size_t getId() const
Definition User.h:60
File * find(const String &path, File *pStartNode=0)
Definition VFS.cc:1023
static VFS & instance()
Definition VFS.cc:311
@ Dec
Definition Log.h:126