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