Flow-IPC 3.0.0
Flow-IPC project: Full implementation reference.
process_credentials.cpp
Go to the documentation of this file.
1/* Flow-IPC: Core
2 * Copyright 2023 Akamai Technologies, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the
5 * "License"); you may not use this file except in
6 * compliance with the License. You may obtain a copy
7 * of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in
12 * writing, software distributed under the License is
13 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
14 * CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing
16 * permissions and limitations under the License. */
17
18/// @file
20#include <flow/error/error.hpp>
21#include <flow/common.hpp>
22#include <boost/filesystem/fstream.hpp>
23#include <boost/lexical_cast.hpp>
24#include <cassert>
25#include <cerrno>
26#include <ostream>
27#include <string>
28#include <unistd.h>
29
30namespace ipc::util
31{
32
33// Global initializations.
34
36
37// Process_credentials implementations.
38
40 Process_credentials(0, 0, 0)
41{
42 // That's it.
43}
44
46{
47 /* I don't really want to rely on the ordering of this annoying little Linux/whatever structure,
48 * so just assign it manually instead of in the initializer above.
49 * @todo In C++20 can use the { .x = ..., .y = ... } syntax. In C++17 clang/gcc allow it anyway... but... etc. */
50 m_val.pid = process_id_init;
51 m_val.uid = user_id_init;
52 m_val.gid = group_id_init;
53}
54
57
59{
60 return m_val.pid;
61}
62
64{
65 return m_val.uid;
66}
67
69{
70 return m_val.gid;
71}
72
74{
75 using boost::lexical_cast;
76 using boost::system::system_category;
77 using fs::path;
78 using fs::ifstream;
79 using std::ios_base;
80 using std::string;
81 // using ::errno; // It's a macro apparently.
82
83 FLOW_ERROR_EXEC_AND_THROW_ON_ERROR(string, process_invoked_as, _1);
84 // ^-- Call ourselves and return if err_code is null. If got to present line, err_code is not null.
85
86#ifndef FLOW_OS_LINUX
87 static_assert(false, "process_invoked_as() depends on Linux /proc semantics.");
88#endif
89
90 path cmd_line_path{"/proc"};
91 cmd_line_path /= lexical_cast<path>(process_id());
92 cmd_line_path /= "cmdline";
93
94 /* @todo Below in a couple places we take errno as the reason for an ifstream failure. That is not guaranteed by the
95 * standard; but in practice both libstdc++ and libc++ (whose filebuf uses fopen()/fread()) leave the libc errno
96 * in place. If errno were somehow 0, we'd emit a success code alongside an empty result. Nicer: set errno = 0
97 * before each op; if still 0 upon failure emit a dedicated util:: error code (cousin of
98 * transport::error::Code::S_MQ_BIPC_MISC_LIBRARY_ERROR) -- which requires a util:: error table. */
99
100 /* This weird "file" (I believe it's really the kernel just pumping out these characters, as we read, as opposed
101 * to anything actually being stored on a drive) is argv[0], argv[1], ..., right in a row, each one NUL-terminated.
102 * So open in binary mode and just read up to NUL or end-of-file (EOF). */
103 ifstream cmd_line_file(cmd_line_path, ios_base::binary);
104 if (!cmd_line_file.good())
105 {
106 *err_code = {errno, system_category()};
107 return {};
108 }
109 // else
110
111 /* From this point on we are quite tolerant; we only emit an error if we see the file in non-.good() state --
112 * but only if it's not end-of-file. Beyond that we make zero assumptions about what might be in here (spaces?
113 * EOF without NUL? whatever!); see background in our doc header impl section. Even an empty return value
114 * is not, in itself, an error (see our contract).
115 *
116 * Detail: We could use .getline(), but I don't want to deal with assumptions of max-length.
117 * We could use string<<, but isspace() has locale implications, and I just don't wanna get into all that.
118 * Reading one char at a time from a buffered stream should be fine perf-wise in this context. */
119 string result;
120 bool done = false;
121 do
122 {
123 const int ch_or_none = cmd_line_file.get();
124 if (!(done = (!cmd_line_file.good()) || (char(ch_or_none) == '\0')))
125 {
126 result += ch_or_none;
127 }
128 // else { We break out of loop. }
129 }
130 while (!done);
131
132 if ((!cmd_line_file.good()) && (!cmd_line_file.eof()))
133 {
134 assert((errno != 0) && "There had to be *some* reason file reading failed, and it wasn't EOF.");
135 *err_code = {errno, system_category()};
136 return {};
137 }
138 // else
139
140 err_code->clear();
141 return result; // Might still be empty (unlikely though)... but as noted -- that's OK by us.
142} // Process_credentials::process_invoked_as()
143
145{
146 return ::getpid();
147}
148
150{
151 return ::geteuid();
152}
153
155{
156 return ::getegid();
157}
158
160{
162}
163
165{
166 return (val1.process_id() == val2.process_id())
167 && (val1.user_id() == val2.user_id()) && (val1.group_id() == val2.group_id());
168}
169
171{
172 return !operator==(val1, val2);
173}
174
175std::ostream& operator<<(std::ostream& os, const Process_credentials& val)
176{
177 const auto pid = val.process_id();
178 const auto uid = val.user_id();
179 const auto gid = val.group_id();
180 if ((pid == 0) && (uid == 0) && (gid == 0))
181 {
182 return os << "null";
183 /* Note, we've intentionally not added `operator bool` or `.empty()` (etc.) to Process_credentials as a logical
184 * convention; it's deliberately minimal/low-level; user can worry about exact interpretation of the individual
185 * values. However, for pithy output -- and that thing only -- we've allowed ourselves the convention to represent
186 * an object that is as-if-default-cted as "null" as a string. */
187 }
188 // else
189 return os << "pid[" << pid << "], user[" << uid << ':' << gid << ']';
190}
191
192} // namespace ipc::util
A process's credentials (PID, UID, GID as of this writing).
process_id_t process_id() const
The process ID (PID).
static process_id_t own_process_id()
Obtains the calling process's process_id().
Process_credentials()
Default ctor: each value is initialized to zero/OS-appropriate equivalent.
static Process_credentials own_process_credentials()
Constructs and returns Process_credentials containing values pertaining to the calling process at thi...
::ucred m_val
The raw data. By using Linux ucred directly, we can reuse this as base for Opt_peer_process_credentia...
Process_credentials & operator=(const Process_credentials &src)
Boring copy assignment.
std::string process_invoked_as(Error_code *err_code=nullptr) const
Obtains, from the OS, information as to the binary name via which process process_id() was started,...
static user_id_t own_user_id()
Obtains the calling process's effective user_id().
static group_id_t own_group_id()
Obtains the calling process's effective group_id().
user_id_t user_id() const
The user ID (UID).
group_id_t group_id() const
The group ID (GID).
Flow-IPC module containing miscellaneous general-use facilities that are ubiquitously used by ~all Fl...
Definition: util.cpp:31
::pid_t process_id_t
Syntactic-sugary type for POSIX process ID (integer).
Definition: util_fwd.hpp:140
std::ostream & operator<<(std::ostream &os, const Native_handle &val)
Prints string representation of the given Native_handle to the given ostream.
::gid_t group_id_t
Syntactic-sugary type for POSIX group ID (integer).
Definition: util_fwd.hpp:146
::uid_t user_id_t
Syntactic-sugary type for POSIX user ID (integer).
Definition: util_fwd.hpp:143
const Process_credentials NULL_PROCESS_CREDENTIALS
A (default-cted) Process_credentials. May be useful for functions returning const Process_credentials...
constexpr bool operator!=(Native_handle val1, Native_handle val2) noexcept
Negation of similar ==.
constexpr bool operator==(Native_handle val1, Native_handle val2) noexcept
Returns true if and only if the two Native_handle objects are the same underlying handle.
flow::Error_code Error_code
Short-hand for flow::Error_code which is very common.
Definition: common.hpp:313