1 /*
2 * Copyright 2015-2024 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> // pid_t, getpid()
14 #include <stdbool.h> // bool
15 #include <stdint.h> // uint8_t, uint64_t
16
17 #include <glib.h> // guint, GList, GHashTable
18 #include <libxml/tree.h> // xmlNode
19
20 #include <crm/common/util.h> // crm_strdup_printf()
21 #include <crm/common/logging.h> // do_crm_log_unlikely(), etc.
22 #include <crm/common/mainloop.h> // mainloop_io_t, struct ipc_client_callbacks
23 #include <crm/common/actions_internal.h>
24 #include <crm/common/digests_internal.h>
25 #include <crm/common/health_internal.h>
26 #include <crm/common/io_internal.h>
27 #include <crm/common/iso8601_internal.h>
28 #include <crm/common/results_internal.h>
29 #include <crm/common/messages_internal.h>
30 #include <crm/common/nvpair_internal.h>
31 #include <crm/common/scores_internal.h>
32 #include <crm/common/strings_internal.h>
33 #include <crm/common/acl_internal.h>
34
35 /* This says whether the current application is a Pacemaker daemon or not,
36 * and is used to change default logging settings such as whether to log to
37 * stderr, etc., as well as a few other details such as whether blackbox signal
38 * handling is enabled.
39 *
40 * It is set when logging is initialized, and does not need to be set directly.
41 */
42 extern bool pcmk__is_daemon;
43
44 //! Node name of the local node
45 extern char *pcmk__our_nodename;
46
47 // Number of elements in a statically defined array
48 #define PCMK__NELEM(a) ((int) (sizeof(a)/sizeof(a[0])) )
49
50 #if SUPPORT_CIBSECRETS
51 /* internal CIB utilities (from cib_secrets.c) */
52
53 int pcmk__substitute_secrets(const char *rsc_id, GHashTable *params);
54 #endif
55
56
57 /* internal main loop utilities (from mainloop.c) */
58
59 int pcmk__add_mainloop_ipc(crm_ipc_t *ipc, int priority, void *userdata,
60 const struct ipc_client_callbacks *callbacks,
61 mainloop_io_t **source);
62 guint pcmk__mainloop_timer_get_period(const mainloop_timer_t *timer);
63
64
65 /* internal node-related XML utilities (from nodes.c) */
66
67 /*!
68 * \internal
69 * \brief Add local node name and ID to an XML node
70 *
71 * \param[in,out] request XML node to modify
72 * \param[in] node The local node's name
73 * \param[in] nodeid The local node's ID (can be 0)
74 */
75 void pcmk__xe_add_node(xmlNode *xml, const char *node, int nodeid);
76
77
78 /* internal name/value utilities (from nvpair.c) */
79
80 int pcmk__scan_nvpair(const char *input, char **name, char **value);
81 char *pcmk__format_nvpair(const char *name, const char *value,
82 const char *units);
83
84 /*!
85 * \internal
86 * \brief Add a boolean attribute to an XML node.
87 *
88 * \param[in,out] node XML node to add attributes to
89 * \param[in] name XML attribute to create
90 * \param[in] value Value to give to the attribute
91 */
92 void
93 pcmk__xe_set_bool_attr(xmlNodePtr node, const char *name, bool value);
94
95 /*!
96 * \internal
97 * \brief Extract a boolean attribute's value from an XML element
98 *
99 * \param[in] node XML node to get attribute from
100 * \param[in] name XML attribute to get
101 *
102 * \return True if the given \p name is an attribute on \p node and has
103 * the value \c PCMK_VALUE_TRUE, False in all other cases
104 */
105 bool
106 pcmk__xe_attr_is_true(const xmlNode *node, const char *name);
107
108 /*!
109 * \internal
110 * \brief Extract a boolean attribute's value from an XML element, with
111 * error checking
112 *
113 * \param[in] node XML node to get attribute from
114 * \param[in] name XML attribute to get
115 * \param[out] value Destination for the value of the attribute
116 *
117 * \return EINVAL if \p name or \p value are NULL, ENODATA if \p node is
118 * NULL or the attribute does not exist, pcmk_rc_unknown_format
119 * if the attribute is not a boolean, and pcmk_rc_ok otherwise.
120 *
121 * \note \p value only has any meaning if the return value is pcmk_rc_ok.
122 */
123 int
124 pcmk__xe_get_bool_attr(const xmlNode *node, const char *name, bool *value);
125
126
127 /* internal procfs utilities (from procfs.c) */
128
129 pid_t pcmk__procfs_pid_of(const char *name);
130 unsigned int pcmk__procfs_num_cores(void);
131 int pcmk__procfs_pid2path(pid_t pid, char path[], size_t path_size);
132 bool pcmk__procfs_has_pids(void);
133
134 /* internal functions related to process IDs (from pid.c) */
135
136 /*!
137 * \internal
138 * \brief Check whether process exists (by PID and optionally executable path)
139 *
140 * \param[in] pid PID of process to check
141 * \param[in] daemon If not NULL, path component to match with procfs entry
142 *
143 * \return Standard Pacemaker return code
144 * \note Particular return codes of interest include pcmk_rc_ok for alive,
145 * ESRCH for process is not alive (verified by kill and/or executable path
146 * match), EACCES for caller unable or not allowed to check. A result of
147 * "alive" is less reliable when \p daemon is not provided or procfs is
148 * not available, since there is no guarantee that the PID has not been
149 * recycled for another process.
150 * \note This function cannot be used to verify \e authenticity of the process.
151 */
152 int pcmk__pid_active(pid_t pid, const char *daemon);
153
154 int pcmk__read_pidfile(const char *filename, pid_t *pid);
155 int pcmk__pidfile_matches(const char *filename, pid_t expected_pid,
156 const char *expected_name, pid_t *pid);
157 int pcmk__lock_pidfile(const char *filename, const char *name);
158
159
160 // bitwise arithmetic utilities
161
162 /*!
163 * \internal
164 * \brief Set specified flags in a flag group
165 *
166 * \param[in] function Function name of caller
167 * \param[in] line Line number of caller
168 * \param[in] log_level Log a message at this level
169 * \param[in] flag_type Label describing this flag group (for logging)
170 * \param[in] target Name of object whose flags these are (for logging)
171 * \param[in] flag_group Flag group being manipulated
172 * \param[in] flags Which flags in the group should be set
173 * \param[in] flags_str Readable equivalent of \p flags (for logging)
174 *
175 * \return Possibly modified flag group
176 */
177 static inline uint64_t
178 pcmk__set_flags_as(const char *function, int line, uint8_t log_level,
/* ![[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)
*/
179 const char *flag_type, const char *target,
180 uint64_t flag_group, uint64_t flags, const char *flags_str)
181 {
182 uint64_t result = flag_group | flags;
183
184 if (result != flag_group) {
185 do_crm_log_unlikely(log_level,
186 "%s flags %#.8llx (%s) for %s set by %s:%d",
187 ((flag_type == NULL)? "Group of" : flag_type),
188 (unsigned long long) flags,
189 ((flags_str == NULL)? "flags" : flags_str),
190 ((target == NULL)? "target" : target),
191 function, line);
192 }
193 return result;
194 }
195
196 /*!
197 * \internal
198 * \brief Clear specified flags in a flag group
199 *
200 * \param[in] function Function name of caller
201 * \param[in] line Line number of caller
202 * \param[in] log_level Log a message at this level
203 * \param[in] flag_type Label describing this flag group (for logging)
204 * \param[in] target Name of object whose flags these are (for logging)
205 * \param[in] flag_group Flag group being manipulated
206 * \param[in] flags Which flags in the group should be cleared
207 * \param[in] flags_str Readable equivalent of \p flags (for logging)
208 *
209 * \return Possibly modified flag group
210 */
211 static inline uint64_t
212 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)
*/
213 const char *flag_type, const char *target,
214 uint64_t flag_group, uint64_t flags, const char *flags_str)
215 {
216 uint64_t result = flag_group & ~flags;
217
218 if (result != flag_group) {
219 do_crm_log_unlikely(log_level,
220 "%s flags %#.8llx (%s) for %s cleared by %s:%d",
221 ((flag_type == NULL)? "Group of" : flag_type),
222 (unsigned long long) flags,
223 ((flags_str == NULL)? "flags" : flags_str),
224 ((target == NULL)? "target" : target),
225 function, line);
226 }
227 return result;
228 }
229
230 /*!
231 * \internal
232 * \brief Get readable string for whether specified flags are set
233 *
234 * \param[in] flag_group Group of flags to check
235 * \param[in] flags Which flags in \p flag_group should be checked
236 *
237 * \return "true" if all \p flags are set in \p flag_group, otherwise "false"
238 */
239 static inline const char *
240 pcmk__flag_text(uint64_t flag_group, uint64_t 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)
*/
241 {
242 return pcmk__btoa(pcmk_all_flags_set(flag_group, flags));
243 }
244
245
246 // miscellaneous utilities (from utils.c)
247
248 void pcmk__daemonize(const char *name, const char *pidfile);
249 void pcmk__panic(const char *origin);
250 pid_t pcmk__locate_sbd(void);
251 void pcmk__sleep_ms(unsigned int ms);
252
253 extern int pcmk__score_red;
254 extern int pcmk__score_green;
255 extern int pcmk__score_yellow;
256
257 /*!
258 * \internal
259 * \brief Allocate new zero-initialized memory, asserting on failure
260 *
261 * \param[in] file File where \p function is located
262 * \param[in] function Calling function
263 * \param[in] line Line within \p file
264 * \param[in] nmemb Number of elements to allocate memory for
265 * \param[in] size Size of each element
266 *
267 * \return Newly allocated memory of of size <tt>nmemb * size</tt> (guaranteed
268 * not to be \c NULL)
269 *
270 * \note The caller is responsible for freeing the return value using \c free().
271 */
272 static inline void *
273 pcmk__assert_alloc_as(const char *file, const char *function, uint32_t line,
/* ![[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)
*/
274 size_t nmemb, size_t size)
275 {
276 void *ptr = calloc(nmemb, size);
277
278 if (ptr == NULL) {
279 crm_abort(file, function, line, "Out of memory", FALSE, TRUE);
280 crm_exit(CRM_EX_OSERR);
281 }
282 return ptr;
283 }
284
285 /*!
286 * \internal
287 * \brief Allocate new zero-initialized memory, asserting on failure
288 *
289 * \param[in] nmemb Number of elements to allocate memory for
290 * \param[in] size Size of each element
291 *
292 * \return Newly allocated memory of of size <tt>nmemb * size</tt> (guaranteed
293 * not to be \c NULL)
294 *
295 * \note The caller is responsible for freeing the return value using \c free().
296 */
297 #define pcmk__assert_alloc(nmemb, size) \
298 pcmk__assert_alloc_as(__FILE__, __func__, __LINE__, nmemb, size)
299
300 /*!
301 * \internal
302 * \brief Resize a dynamically allocated memory block
303 *
304 * \param[in] ptr Memory block to resize (or NULL to allocate new memory)
305 * \param[in] size New size of memory block in bytes (must be > 0)
306 *
307 * \return Pointer to resized memory block
308 *
309 * \note This asserts on error, so the result is guaranteed to be non-NULL
310 * (which is the main advantage of this over directly using realloc()).
311 */
312 static inline void *
313 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)
*/
314 {
315 void *new_ptr;
316
317 // realloc(p, 0) can replace free(p) but this wrapper can't
318 pcmk__assert(size > 0);
319
320 new_ptr = realloc(ptr, size);
321 if (new_ptr == NULL) {
322 free(ptr);
323 abort();
324 }
325 return new_ptr;
326 }
327
328 static inline char *
329 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)
*/
330 {
331 return crm_strdup_printf("%lu", (unsigned long) getpid());
332 }
333
334 // More efficient than g_list_length(list) == 1
335 static inline bool
336 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)
*/
337 {
338 return list && (list->next == NULL);
339 }
340
341 // More efficient than g_list_length(list) > 1
342 static inline bool
343 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)
*/
344 {
345 return list && (list->next != NULL);
346 }
347
348 /* convenience functions for failure-related node attributes */
349
350 #define PCMK__FAIL_COUNT_PREFIX "fail-count"
351 #define PCMK__LAST_FAILURE_PREFIX "last-failure"
352
353 /*!
354 * \internal
355 * \brief Generate a failure-related node attribute name for a resource
356 *
357 * \param[in] prefix Start of attribute name
358 * \param[in] rsc_id Resource name
359 * \param[in] op Operation name
360 * \param[in] interval_ms Operation interval
361 *
362 * \return Newly allocated string with attribute name
363 *
364 * \note Failure attributes are named like PREFIX-RSC#OP_INTERVAL (for example,
365 * "fail-count-myrsc#monitor_30000"). The '#' is used because it is not
366 * a valid character in a resource ID, to reliably distinguish where the
367 * operation name begins. The '_' is used simply to be more comparable to
368 * action labels like "myrsc_monitor_30000".
369 */
370 static inline char *
371 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)
*/
372 guint interval_ms)
373 {
374 CRM_CHECK(prefix && rsc_id && op, return NULL);
375 return crm_strdup_printf("%s-%s#%s_%u", prefix, rsc_id, op, interval_ms);
376 }
377
378 static inline char *
379 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)
*/
380 {
381 return pcmk__fail_attr_name(PCMK__FAIL_COUNT_PREFIX, rsc_id, op,
382 interval_ms);
383 }
384
385 static inline char *
386 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)
*/
387 {
388 return pcmk__fail_attr_name(PCMK__LAST_FAILURE_PREFIX, rsc_id, op,
389 interval_ms);
390 }
391
392 // internal resource agent functions (from agents.c)
393 int pcmk__effective_rc(int rc);
394
395 #endif /* CRM_COMMON_INTERNAL__H */