1 /*
2 * Copyright 2015-2020 the Pacemaker project contributors
3 *
4 * The version control history for this file may have further details.
5 *
6 * This source code is licensed under the GNU Lesser General Public License
7 * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8 */
9
10 #ifndef CRM_COMMON_INTERNAL__H
11 #define CRM_COMMON_INTERNAL__H
12
13 #include <unistd.h> // getpid()
14 #include <stdbool.h> // bool
15 #include <stdint.h> // uint8_t, uint64_t
16 #include <string.h> // strcmp()
17 #include <fcntl.h> // open()
18 #include <sys/types.h> // uid_t, gid_t, pid_t
19
20 #include <glib.h> // guint, GList, GHashTable
21 #include <libxml/tree.h> // xmlNode
22
23 #include <crm/common/util.h> // crm_strdup_printf()
24 #include <crm/common/logging.h> // do_crm_log_unlikely(), etc.
25 #include <crm/common/mainloop.h> // mainloop_io_t, struct ipc_client_callbacks
26 #include <crm/common/strings_internal.h>
27
28 // Internal ACL-related utilities (from acl.c)
29
30 char *pcmk__uid2username(uid_t uid);
31 const char *pcmk__update_acl_user(xmlNode *request, const char *field,
32 const char *peer_user);
33
34 #if ENABLE_ACL
35 # include <string.h>
36 static inline bool
37 pcmk__is_privileged(const char *user)
/* ![[previous]](../icons/n_left.png)
![[next]](../icons/right.png)
![[first]](../icons/n_first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
38 {
39 return user && (!strcmp(user, CRM_DAEMON_USER) || !strcmp(user, "root"));
40 }
41 #endif
42
43
44 #if SUPPORT_CIBSECRETS
45 // Internal CIB utilities (from cib_secrets.c) */
46
47 int pcmk__substitute_secrets(const char *rsc_id, GHashTable *params);
48 #endif
49
50
51 /* internal digest-related utilities (from digest.c) */
52
53 bool pcmk__verify_digest(xmlNode *input, const char *expected);
54
55
56 /* internal I/O utilities (from io.c) */
57
58 int pcmk__real_path(const char *path, char **resolved_path);
59
60 char *pcmk__series_filename(const char *directory, const char *series,
61 int sequence, bool bzip);
62 int pcmk__read_series_sequence(const char *directory, const char *series,
63 unsigned int *seq);
64 void pcmk__write_series_sequence(const char *directory, const char *series,
65 unsigned int sequence, int max);
66 int pcmk__chown_series_sequence(const char *directory, const char *series,
67 uid_t uid, gid_t gid);
68
69 int pcmk__build_path(const char *path_c, mode_t mode);
70 bool pcmk__daemon_can_write(const char *dir, const char *file);
71 void pcmk__sync_directory(const char *name);
72
73 int pcmk__file_contents(const char *filename, char **contents);
74 int pcmk__write_sync(int fd, const char *contents);
75 int pcmk__set_nonblocking(int fd);
76 const char *pcmk__get_tmpdir(void);
77
78 void pcmk__close_fds_in_child(bool);
79
80 /*!
81 * \internal
82 * \brief Open /dev/null to consume next available file descriptor
83 *
84 * Open /dev/null, disregarding the result. This is intended when daemonizing to
85 * be able to null stdin, stdout, and stderr.
86 *
87 * \param[in] flags O_RDONLY (stdin) or O_WRONLY (stdout and stderr)
88 */
89 static inline void
90 pcmk__open_devnull(int flags)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
91 {
92 // Static analysis clutter
93 // cppcheck-suppress leakReturnValNotUsed
94 (void) open("/dev/null", flags);
95 }
96
97
98 /* internal logging utilities */
99
100 /*!
101 * \internal
102 * \brief Log a configuration error
103 *
104 * \param[in] fmt printf(3)-style format string
105 * \param[in] ... Arguments for format string
106 */
107 # define pcmk__config_err(fmt...) do { \
108 pcmk__config_error = true; \
109 crm_err(fmt); \
110 } while (0)
111
112 /*!
113 * \internal
114 * \brief Log a configuration warning
115 *
116 * \param[in] fmt printf(3)-style format string
117 * \param[in] ... Arguments for format string
118 */
119 # define pcmk__config_warn(fmt...) do { \
120 pcmk__config_warning = true; \
121 crm_warn(fmt); \
122 } while (0)
123
124 /*!
125 * \internal
126 * \brief Execute code depending on whether message would be logged
127 *
128 * This is similar to do_crm_log_unlikely() except instead of logging, it either
129 * continues past this statement or executes else_action depending on whether a
130 * message of the given severity would be logged or not. This allows whole
131 * blocks of code to be skipped if tracing or debugging is turned off.
132 *
133 * \param[in] level Severity at which to continue past this statement
134 * \param[in] else_action Code block to execute if severity would not be logged
135 *
136 * \note else_action must not contain a break or continue statement
137 */
138 # define pcmk__log_else(level, else_action) do { \
139 static struct qb_log_callsite *trace_cs = NULL; \
140 \
141 if (trace_cs == NULL) { \
142 trace_cs = qb_log_callsite_get(__func__, __FILE__, "log_else", \
143 level, __LINE__, 0); \
144 } \
145 if (!crm_is_callsite_active(trace_cs, level, 0)) { \
146 else_action; \
147 } \
148 } while(0)
149
150
151 /* internal main loop utilities (from mainloop.c) */
152
153 int pcmk__add_mainloop_ipc(crm_ipc_t *ipc, int priority, void *userdata,
154 struct ipc_client_callbacks *callbacks,
155 mainloop_io_t **source);
156
157
158 /* internal messaging utilities (from messages.c) */
159
160 const char *pcmk__message_name(const char *name);
161
162
163 /* internal procfs utilities (from procfs.c) */
164
165 pid_t pcmk__procfs_pid_of(const char *name);
166 unsigned int pcmk__procfs_num_cores(void);
167
168
169 /* internal XML schema functions (from xml.c) */
170
171 void crm_schema_init(void);
172 void crm_schema_cleanup(void);
173
174
175 /* internal functions related to process IDs (from pid.c) */
176
177 /*!
178 * \internal
179 * \brief Check whether process exists (by PID and optionally executable path)
180 *
181 * \param[in] pid PID of process to check
182 * \param[in] daemon If not NULL, path component to match with procfs entry
183 *
184 * \return Standard Pacemaker return code
185 * \note Particular return codes of interest include pcmk_rc_ok for alive,
186 * ESRCH for process is not alive (verified by kill and/or executable path
187 * match), EACCES for caller unable or not allowed to check. A result of
188 * "alive" is less reliable when \p daemon is not provided or procfs is
189 * not available, since there is no guarantee that the PID has not been
190 * recycled for another process.
191 * \note This function cannot be used to verify \e authenticity of the process.
192 */
193 int pcmk__pid_active(pid_t pid, const char *daemon);
194
195 int pcmk__read_pidfile(const char *filename, pid_t *pid);
196 int pcmk__pidfile_matches(const char *filename, pid_t expected_pid,
197 const char *expected_name, pid_t *pid);
198 int pcmk__lock_pidfile(const char *filename, const char *name);
199
200
201 /* internal functions related to resource operations (from operations.c) */
202
203 // printf-style format to create operation ID from resource, action, interval
204 #define PCMK__OP_FMT "%s_%s_%u"
205
206 char *pcmk__op_key(const char *rsc_id, const char *op_type, guint interval_ms);
207 char *pcmk__notify_key(const char *rsc_id, const char *notify_type,
208 const char *op_type);
209 char *pcmk__transition_key(int transition_id, int action_id, int target_rc,
210 const char *node);
211 void pcmk__filter_op_for_digest(xmlNode *param_set);
212
213
214 // bitwise arithmetic utilities
215
216 /*!
217 * \internal
218 * \brief Set specified flags in a flag group
219 *
220 * \param[in] function Function name of caller
221 * \param[in] line Line number of caller
222 * \param[in] log_level Log a message at this level
223 * \param[in] flag_type Label describing this flag group (for logging)
224 * \param[in] target Name of object whose flags these are (for logging)
225 * \param[in] flag_group Flag group being manipulated
226 * \param[in] flags Which flags in the group should be set
227 * \param[in] flags_str Readable equivalent of \p flags (for logging)
228 *
229 * \return Possibly modified flag group
230 */
231 static inline uint64_t
232 pcmk__set_flags_as(const char *function, int line, uint8_t log_level,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
233 const char *flag_type, const char *target,
234 uint64_t flag_group, uint64_t flags, const char *flags_str)
235 {
236 uint64_t result = flag_group | flags;
237
238 if (result != flag_group) {
239 do_crm_log_unlikely(log_level,
240 "%s flags 0x%.8llx (%s) for %s set by %s:%d",
241 ((flag_type == NULL)? "Group of" : flag_type),
242 (unsigned long long) flags,
243 ((flags_str == NULL)? "flags" : flags_str),
244 ((target == NULL)? "target" : target),
245 function, line);
246 }
247 return result;
248 }
249
250 /*!
251 * \internal
252 * \brief Clear specified flags in a flag group
253 *
254 * \param[in] function Function name of caller
255 * \param[in] line Line number of caller
256 * \param[in] log_level Log a message at this level
257 * \param[in] flag_type Label describing this flag group (for logging)
258 * \param[in] target Name of object whose flags these are (for logging)
259 * \param[in] flag_group Flag group being manipulated
260 * \param[in] flags Which flags in the group should be cleared
261 * \param[in] flags_str Readable equivalent of \p flags (for logging)
262 *
263 * \return Possibly modified flag group
264 */
265 static inline uint64_t
266 pcmk__clear_flags_as(const char *function, int line, uint8_t log_level,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
267 const char *flag_type, const char *target,
268 uint64_t flag_group, uint64_t flags, const char *flags_str)
269 {
270 uint64_t result = flag_group & ~flags;
271
272 if (result != flag_group) {
273 do_crm_log_unlikely(log_level,
274 "%s flags 0x%.8llx (%s) for %s cleared by %s:%d",
275 ((flag_type == NULL)? "Group of" : flag_type),
276 (unsigned long long) flags,
277 ((flags_str == NULL)? "flags" : flags_str),
278 ((target == NULL)? "target" : target),
279 function, line);
280 }
281 return result;
282 }
283
284 // miscellaneous utilities (from utils.c)
285
286 void pcmk__daemonize(const char *name, const char *pidfile);
287 void pcmk__panic(const char *origin);
288 pid_t pcmk__locate_sbd(void);
289
290 extern int pcmk__score_red;
291 extern int pcmk__score_green;
292 extern int pcmk__score_yellow;
293
294 /*!
295 * \internal
296 * \brief Resize a dynamically allocated memory block
297 *
298 * \param[in] ptr Memory block to resize (or NULL to allocate new memory)
299 * \param[in] size New size of memory block in bytes (must be > 0)
300 *
301 * \return Pointer to resized memory block
302 *
303 * \note This asserts on error, so the result is guaranteed to be non-NULL
304 * (which is the main advantage of this over directly using realloc()).
305 */
306 static inline void *
307 pcmk__realloc(void *ptr, size_t size)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
308 {
309 void *new_ptr;
310
311 // realloc(p, 0) can replace free(p) but this wrapper can't
312 CRM_ASSERT(size > 0);
313
314 new_ptr = realloc(ptr, size);
315 if (new_ptr == NULL) {
316 free(ptr);
317 abort();
318 }
319 return new_ptr;
320 }
321
322
323 /* Error domains for use with g_set_error (from results.c) */
324
325 GQuark pcmk__rc_error_quark(void);
326 GQuark pcmk__exitc_error_quark(void);
327
328 #define PCMK__RC_ERROR pcmk__rc_error_quark()
329 #define PCMK__EXITC_ERROR pcmk__exitc_error_quark()
330
331 static inline char *
332 pcmk__getpid_s(void)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
333 {
334 return crm_strdup_printf("%lu", (unsigned long) getpid());
335 }
336
337 // More efficient than g_list_length(list) == 1
338 static inline bool
339 pcmk__list_of_1(GList *list)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
340 {
341 return list && (list->next == NULL);
342 }
343
344 // More efficient than g_list_length(list) > 1
345 static inline bool
346 pcmk__list_of_multiple(GList *list)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
347 {
348 return list && (list->next != NULL);
349 }
350
351 /* convenience functions for failure-related node attributes */
352
353 #define PCMK__FAIL_COUNT_PREFIX "fail-count"
354 #define PCMK__LAST_FAILURE_PREFIX "last-failure"
355
356 /*!
357 * \internal
358 * \brief Generate a failure-related node attribute name for a resource
359 *
360 * \param[in] prefix Start of attribute name
361 * \param[in] rsc_id Resource name
362 * \param[in] op Operation name
363 * \param[in] interval_ms Operation interval
364 *
365 * \return Newly allocated string with attribute name
366 *
367 * \note Failure attributes are named like PREFIX-RSC#OP_INTERVAL (for example,
368 * "fail-count-myrsc#monitor_30000"). The '#' is used because it is not
369 * a valid character in a resource ID, to reliably distinguish where the
370 * operation name begins. The '_' is used simply to be more comparable to
371 * action labels like "myrsc_monitor_30000".
372 */
373 static inline char *
374 pcmk__fail_attr_name(const char *prefix, const char *rsc_id, const char *op,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
375 guint interval_ms)
376 {
377 CRM_CHECK(prefix && rsc_id && op, return NULL);
378 return crm_strdup_printf("%s-%s#%s_%u", prefix, rsc_id, op, interval_ms);
379 }
380
381 static inline char *
382 pcmk__failcount_name(const char *rsc_id, const char *op, guint interval_ms)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
383 {
384 return pcmk__fail_attr_name(PCMK__FAIL_COUNT_PREFIX, rsc_id, op,
385 interval_ms);
386 }
387
388 static inline char *
389 pcmk__lastfailure_name(const char *rsc_id, const char *op, guint interval_ms)
/* ![[previous]](../icons/left.png)
![[next]](../icons/n_right.png)
![[first]](../icons/first.png)
![[last]](../icons/n_last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
390 {
391 return pcmk__fail_attr_name(PCMK__LAST_FAILURE_PREFIX, rsc_id, op,
392 interval_ms);
393 }
394
395 // internal resource agent functions (from agents.c)
396 int pcmk__effective_rc(int rc);
397
398 #endif /* CRM_COMMON_INTERNAL__H */