Coverage Report

Created: 2026-04-10 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pacemaker/lib/common/agents.c
Line
Count
Source
1
/*
2
 * Copyright 2004-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
#include <crm_internal.h>
11
12
#include <stdbool.h>
13
#include <stdio.h>
14
#include <string.h>
15
#include <strings.h>
16
17
#include <glib.h>   // g_str_has_prefix()
18
19
#include <crm/crm.h>
20
#include <crm/common/util.h>
21
22
/*!
23
 * \brief Get capabilities of a resource agent standard
24
 *
25
 * \param[in] standard  Standard name
26
 *
27
 * \return Bitmask of enum pcmk_ra_caps values
28
 */
29
uint32_t
30
pcmk_get_ra_caps(const char *standard)
31
0
{
32
    /* @COMPAT This should probably be case-sensitive, but isn't,
33
     * for backward compatibility.
34
     */
35
0
    if (standard == NULL) {
36
0
        return pcmk_ra_cap_none;
37
38
0
    } else if (!strcasecmp(standard, PCMK_RESOURCE_CLASS_OCF)) {
39
0
        return pcmk_ra_cap_provider | pcmk_ra_cap_params
40
0
               | pcmk_ra_cap_unique | pcmk_ra_cap_promotable
41
0
               | pcmk_ra_cap_cli_exec;
42
43
0
    } else if (!strcasecmp(standard, PCMK_RESOURCE_CLASS_STONITH)) {
44
        /* @COMPAT Stonith resources can't really be unique clones, but we've
45
         * allowed it in the past and have it in some scheduler regression tests
46
         * (which were likely never used as real configurations).
47
         *
48
         * @TODO Remove pcmk_ra_cap_unique at the next major schema version
49
         * bump, with a transform to remove PCMK_META_GLOBALLY_UNIQUE from the
50
         * config.
51
         */
52
0
        return pcmk_ra_cap_params | pcmk_ra_cap_unique | pcmk_ra_cap_stdin
53
0
               | pcmk_ra_cap_fence_params;
54
55
0
    } else if (!strcasecmp(standard, PCMK_RESOURCE_CLASS_LSB)) {
56
0
        return pcmk_ra_cap_status | pcmk_ra_cap_cli_exec;
57
58
0
    } else if (!strcasecmp(standard, PCMK_RESOURCE_CLASS_SYSTEMD)
59
0
               || !strcasecmp(standard, PCMK_RESOURCE_CLASS_SERVICE)) {
60
0
        return pcmk_ra_cap_status;
61
0
    }
62
0
    return pcmk_ra_cap_none;
63
0
}
64
65
int
66
pcmk__effective_rc(int rc)
67
0
{
68
0
    int remapped_rc = rc;
69
70
0
    switch (rc) {
71
0
        case PCMK_OCF_DEGRADED:
72
0
            remapped_rc = PCMK_OCF_OK;
73
0
            break;
74
75
0
        case PCMK_OCF_DEGRADED_PROMOTED:
76
0
            remapped_rc = PCMK_OCF_RUNNING_PROMOTED;
77
0
            break;
78
79
0
        default:
80
0
            break;
81
0
    }
82
83
0
    return remapped_rc;
84
0
}
85
86
char *
87
crm_generate_ra_key(const char *standard, const char *provider,
88
                    const char *type)
89
0
{
90
0
    bool std_empty = pcmk__str_empty(standard);
91
0
    bool prov_empty = pcmk__str_empty(provider);
92
0
    bool ty_empty = pcmk__str_empty(type);
93
94
0
    if (std_empty || ty_empty) {
95
0
        return NULL;
96
0
    }
97
98
0
    return pcmk__assert_asprintf("%s%s%s:%s",
99
0
                                 standard,
100
0
                                 (prov_empty ? "" : ":"),
101
0
                                 (prov_empty ? "" : provider),
102
0
                                 type);
103
0
}
104
105
/*!
106
 * \brief Parse a "standard[:provider]:type" agent specification
107
 *
108
 * \param[in]  spec      Agent specification
109
 * \param[out] standard  Where to store agent standard (may not be \c NULL)
110
 * \param[out] provider  Where to store agent provider if the standard supports
111
 *                       one (may not be \c NULL)
112
 * \param[put] type      Where to store agent type (may not be \c NULL)
113
 *
114
 * \return \c pcmk_ok if the string could be parsed, \c -EINVAL otherwise
115
 *
116
 * \note It is acceptable for the type to contain a ':' if the standard supports
117
 *       that. For example, systemd supports the form "systemd:UNIT@A:B".
118
 * \note On success, the caller is responsible for freeing \p *standard,
119
 *       \p *provider, and \p *type using \c free(). On failure, all of these
120
 *       are left unchanged.
121
 */
122
int
123
crm_parse_agent_spec(const char *spec, char **standard, char **provider,
124
                     char **type)
125
0
{
126
0
    gchar **parts = NULL;
127
0
    int rc = pcmk_ok;
128
129
0
    CRM_CHECK((spec != NULL) && (standard != NULL) && (provider != NULL)
130
0
              && (type != NULL), return -EINVAL);
131
132
0
    parts = g_strsplit(spec, ":", 3);
133
134
0
    if (pcmk__str_empty(parts[0])) {
135
        // Empty standard
136
0
        rc = -EINVAL;
137
0
        goto done;
138
0
    }
139
140
0
    if (pcmk__is_set(pcmk_get_ra_caps(parts[0]), pcmk_ra_cap_provider)) {
141
0
        if (pcmk__str_empty(parts[1]) || pcmk__str_empty(parts[2])) {
142
            // Empty provider or type
143
0
            rc = -EINVAL;
144
0
            goto done;
145
0
        }
146
147
0
        *standard = pcmk__str_copy(parts[0]);
148
0
        *provider = pcmk__str_copy(parts[1]);
149
0
        *type = pcmk__str_copy(parts[2]);
150
151
0
    } else {
152
0
        if (pcmk__str_empty(parts[1])) {
153
            // Empty type
154
0
            rc = -EINVAL;
155
0
            goto done;
156
0
        }
157
158
0
        *standard = pcmk__str_copy(parts[0]);
159
160
0
        if (parts[2] == NULL) {
161
            // Common case: type does not contain a colon
162
0
            *type = pcmk__str_copy(parts[1]);
163
164
0
        } else {
165
            // Accommodate "systemd:UNIT@A:B", for example
166
0
            gchar *joined = g_strjoinv(":", parts + 1);
167
168
0
            *type = pcmk__str_copy(joined);
169
0
            g_free(joined);
170
0
        }
171
0
    }
172
173
0
done:
174
0
    g_strfreev(parts);
175
0
    return rc;
176
0
}
177
178
/*!
179
 * \brief Check whether a given stonith parameter is handled by Pacemaker
180
 *
181
 * Return true if a given string is the name of one of the special resource
182
 * instance attributes interpreted directly by Pacemaker for stonith-class
183
 * resources.
184
 *
185
 * \param[in] param  Parameter name to check
186
 *
187
 * \return true if \p param is a special fencing parameter
188
 */
189
bool
190
pcmk_stonith_param(const char *param)
191
0
{
192
0
    if (param == NULL) {
193
0
        return false;
194
0
    }
195
196
    /* @COMPAT Pacemaker does not handle PCMK__FENCING_STONITH_TIMEOUT specially
197
     * as a resource parameter, so pcmk_stonith_param() should not return true
198
     * for it. It is unclear from the commit history why we returned true for it
199
     * in the first place.
200
     *
201
     * However, when the feature set is less than 3.16.0,
202
     * calculate_secure_digest() filters out these special fencing parameters
203
     * when calculating the digest. There's no good reason why a user should
204
     * have configured this as a fence resource parameter in the first place.
205
     *
206
     * But out of an abundance of caution, we should wait to drop
207
     * PCMK__FENCING_STONITH_TIMEOUT from this function until we no longer
208
     * support rolling upgrades from below Pacemaker 2.1.5.
209
     */
210
0
    if (pcmk__str_any_of(param, PCMK_FENCING_PROVIDES,
211
0
                         PCMK__FENCING_STONITH_TIMEOUT, NULL)) {
212
0
        return true;
213
0
    }
214
215
0
    if (!g_str_has_prefix(param, "pcmk_")) { // Short-circuit common case
216
0
        return false;
217
0
    }
218
0
    if (pcmk__str_any_of(param,
219
0
                         PCMK_FENCING_ACTION_LIMIT,
220
0
                         PCMK_FENCING_DELAY_BASE,
221
0
                         PCMK_FENCING_DELAY_MAX,
222
0
                         PCMK_FENCING_HOST_ARGUMENT,
223
0
                         PCMK_FENCING_HOST_CHECK,
224
0
                         PCMK_FENCING_HOST_LIST,
225
0
                         PCMK_FENCING_HOST_MAP,
226
0
                         NULL)) {
227
0
        return true;
228
0
    }
229
0
    param = strchr(param + 5, '_'); // Skip past "pcmk_ACTION"
230
    return pcmk__str_any_of(param, "_action", "_timeout", "_retries", NULL);
231
0
}