Skip to content

Commit ea63471

Browse files
committed
API: libcrmservice: Change services_action_user() not-found return code
This is mostly a refactor commit that causes a small change to the libcrmservice API. See final paragraph. Create a new pcmk__lookup_user() to replace crm_user_lookup(). This new function calls getpwnam() instead of getpwnam_r(). Since Pacemaker is single-threaded and doesn't need reentrancy for this call, I can't think of any good reason why we ever needed getpwnam_r(). It requires defining a struct passwd and allocating a buffer to copy its string members into, which makes everything more complicated. getpwnam() is basically equivalent but returns a pointer and sets errno. All we need are the uid and gid members. Most of this commit is actually changes to mocking and unit testing, and having to remember how the expect and will_return stuff works, due to using getpwnam() instead of getpwnam_r(). This changes the specific error return codes from services_action_user() in some cases. The default "no match" return code, if no entry pointer is returned and errno is 0, is now ENOENT. Previously it was EINVAL. I think this is acceptable, even though it technically changes the API. We never documented specific error codes. As long as a caller considers any nonzero code to be an error, nothing changes. (I'm not sure if there's any legitimate use case for an external caller to create or modify a service action directly anyway.) Signed-off-by: Reid Wahl <[email protected]>
1 parent a203c83 commit ea63471

File tree

11 files changed

+238
-191
lines changed

11 files changed

+238
-191
lines changed

daemons/based/based_remote.c

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -151,16 +151,18 @@ static int
151151
check_group_membership(const char *usr, const char *grp)
152152
{
153153
int index = 0;
154-
struct passwd *pwd = NULL;
154+
gid_t gid = 0;
155155
struct group *group = NULL;
156+
int rc = pcmk_rc_ok;
156157

157-
pwd = getpwnam(usr);
158-
if (pwd == NULL) {
159-
crm_notice("Rejecting remote client: '%s' is not a valid user", usr);
158+
rc = pcmk__lookup_user(usr, NULL, &gid);
159+
if (rc != pcmk_rc_ok) {
160+
crm_notice("Rejecting remote client: could not find user '%s': %s",
161+
usr, pcmk_rc_str(rc));
160162
return FALSE;
161163
}
162164

163-
group = getgrgid(pwd->pw_gid);
165+
group = getgrgid(gid);
164166
if (group != NULL && pcmk__str_eq(grp, group->gr_name, pcmk__str_none)) {
165167
return TRUE;
166168
}

include/crm/common/internal.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@
1010
#ifndef PCMK__CRM_COMMON_INTERNAL__H
1111
#define PCMK__CRM_COMMON_INTERNAL__H
1212

13-
#include <unistd.h> // pid_t, getpid()
13+
#include <pwd.h> // struct passwd
14+
#include <unistd.h> // getpid()
1415
#include <stdbool.h> // bool
1516
#include <stdint.h> // uint8_t, uint64_t
17+
#include <sys/types.h> // pid_t, uid_t, gid_t
1618
#include <inttypes.h> // PRIu64
1719

1820
#include <glib.h> // guint, GList, GHashTable
@@ -249,6 +251,7 @@ pcmk__flag_text(uint64_t flag_group, uint64_t flags)
249251
int pcmk__daemon_user(uid_t *uid, gid_t *gid);
250252
void pcmk__daemonize(const char *name, const char *pidfile);
251253
char *pcmk__generate_uuid(void);
254+
int pcmk__lookup_user(const char *name, uid_t *uid, gid_t *gid);
252255
void pcmk__panic(const char *reason);
253256
pid_t pcmk__locate_sbd(void);
254257
void pcmk__sleep_ms(unsigned int ms);

lib/common/mock.c

Lines changed: 12 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -356,41 +356,30 @@ __wrap_fopen64(const char *pathname, const char *mode)
356356
}
357357
#endif
358358

359-
/* getpwnam_r()
359+
/* getpwnam()
360360
*
361-
* If pcmk__mock_getpwnam_r is set to true, later calls to getpwnam_r() must be
361+
* If pcmk__mock_getpwnam is set to true, later calls to getpwnam() must be
362362
* preceded by:
363363
*
364-
* expect_*(__wrap_getpwnam_r, name[, ...]);
365-
* expect_*(__wrap_getpwnam_r, pwd[, ...]);
366-
* expect_*(__wrap_getpwnam_r, buf[, ...]);
367-
* expect_*(__wrap_getpwnam_r, buflen[, ...]);
368-
* expect_*(__wrap_getpwnam_r, result[, ...]);
369-
* will_return(__wrap_getpwnam_r, return_value);
370-
* will_return(__wrap_getpwnam_r, ptr_to_result_struct);
364+
* expect_*(__wrap_getpwnam, name[, ...]);
365+
* will_return(__wrap_getpwnam, errno_to_set);
366+
* will_return(__wrap_getpwnam, ptr_to_result_struct);
371367
*
372368
* expect_* functions: https://api.cmocka.org/group__cmocka__param.html
373369
*/
374370

375-
bool pcmk__mock_getpwnam_r = false;
371+
bool pcmk__mock_getpwnam = false;
376372

377-
int
378-
__wrap_getpwnam_r(const char *name, struct passwd *pwd, char *buf,
379-
size_t buflen, struct passwd **result)
373+
struct passwd *
374+
__wrap_getpwnam(const char *name)
380375
{
381-
if (pcmk__mock_getpwnam_r) {
382-
int retval = mock_type(int);
383-
376+
if (pcmk__mock_getpwnam) {
384377
check_expected_ptr(name);
385-
check_expected_ptr(pwd);
386-
check_expected_ptr(buf);
387-
check_expected(buflen);
388-
check_expected_ptr(result);
389-
*result = mock_ptr_type(struct passwd *);
390-
return retval;
378+
errno = mock_type(int);
379+
return mock_ptr_type(struct passwd *);
391380

392381
} else {
393-
return __real_getpwnam_r(name, pwd, buf, buflen, result);
382+
return __real_getpwnam(name);
394383
}
395384
}
396385

lib/common/mock_private.h

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2021-2024 the Pacemaker project contributors
2+
* Copyright 2021-2025 the Pacemaker project contributors
33
*
44
* The version control history for this file may have further details.
55
*
@@ -74,11 +74,9 @@ struct group * __real_getgrent(void);
7474
void __wrap_endgrent(void);
7575
void __real_endgrent(void);
7676

77-
extern bool pcmk__mock_getpwnam_r;
78-
int __real_getpwnam_r(const char *name, struct passwd *pwd,
79-
char *buf, size_t buflen, struct passwd **result);
80-
int __wrap_getpwnam_r(const char *name, struct passwd *pwd,
81-
char *buf, size_t buflen, struct passwd **result);
77+
extern bool pcmk__mock_getpwnam;
78+
struct passwd *__real_getpwnam(const char *name);
79+
struct passwd *__wrap_getpwnam(const char *name);
8280

8381
extern bool pcmk__mock_readlink;
8482
ssize_t __real_readlink(const char *restrict path, char *restrict buf,

lib/common/tests/utils/Makefile.am

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ include $(top_srcdir)/mk/unittest.mk
1313

1414
# Add "_test" to the end of all test program names to simplify .gitignore.
1515
check_PROGRAMS = compare_version_test \
16-
crm_user_lookup_test \
1716
pcmk__daemon_user_test \
1817
pcmk__fail_attr_name_test \
1918
pcmk__failcount_name_test \
2019
pcmk__getpid_s_test \
2120
pcmk__lastfailure_name_test \
21+
pcmk__lookup_user_test \
2222
pcmk__realloc_test \
2323
pcmk__timeout_ms2s_test
2424

lib/common/tests/utils/crm_user_lookup_test.c

Lines changed: 0 additions & 127 deletions
This file was deleted.

lib/common/tests/utils/pcmk__daemon_user_test.c

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,15 @@ no_matching_pwent(void **state)
2323
uid_t uid = 0;
2424
gid_t gid = 0;
2525

26-
// Set getpwnam_r() return value and result parameter
27-
pcmk__mock_getpwnam_r = true;
26+
pcmk__mock_getpwnam = true;
2827

29-
expect_string(__wrap_getpwnam_r, name, "hacluster");
30-
expect_any(__wrap_getpwnam_r, pwd);
31-
expect_any(__wrap_getpwnam_r, buf);
32-
expect_value(__wrap_getpwnam_r, buflen, PCMK__PW_BUFFER_LEN);
33-
expect_any(__wrap_getpwnam_r, result);
34-
will_return(__wrap_getpwnam_r, ENOENT);
35-
will_return(__wrap_getpwnam_r, NULL);
28+
expect_string(__wrap_getpwnam, name, "hacluster");
29+
will_return(__wrap_getpwnam, 0);
30+
will_return(__wrap_getpwnam, NULL);
3631

3732
assert_int_equal(pcmk__daemon_user(&uid, &gid), ENOENT);
3833

39-
pcmk__mock_getpwnam_r = false;
34+
pcmk__mock_getpwnam = false;
4035
}
4136

4237
static void
@@ -48,25 +43,21 @@ entry_found(void **state)
4843
// We don't care about the other fields of the passwd entry
4944
struct passwd returned_ent = { .pw_uid = 1000, .pw_gid = 1000 };
5045

51-
// Test getpwnam_r() returning a valid passwd entry with null output args
46+
// Test getpwnam() returning a valid passwd entry with null output args
5247

53-
pcmk__mock_getpwnam_r = true;
48+
pcmk__mock_getpwnam = true;
5449

55-
expect_string(__wrap_getpwnam_r, name, "hacluster");
56-
expect_any(__wrap_getpwnam_r, pwd);
57-
expect_any(__wrap_getpwnam_r, buf);
58-
expect_value(__wrap_getpwnam_r, buflen, PCMK__PW_BUFFER_LEN);
59-
expect_any(__wrap_getpwnam_r, result);
60-
will_return(__wrap_getpwnam_r, 0);
61-
will_return(__wrap_getpwnam_r, &returned_ent);
50+
expect_string(__wrap_getpwnam, name, "hacluster");
51+
will_return(__wrap_getpwnam, 0);
52+
will_return(__wrap_getpwnam, &returned_ent);
6253

6354
assert_int_equal(pcmk__daemon_user(NULL, NULL), pcmk_rc_ok);
6455

65-
// Test getpwnam_r() returning a valid passwd entry with non-NULL outputs
56+
// Test getpwnam() returning a valid passwd entry with non-NULL outputs
6657

6758
/* We don't need to call expect_*() or will_return() again because
6859
* pcmk__daemon_user() will have cached the uid/gid from the previous call
69-
* and won't make another call to getpwnam_r().
60+
* and won't make another call to getpwnam().
7061
*/
7162
assert_int_equal(pcmk__daemon_user(&uid, NULL), pcmk_rc_ok);
7263
assert_int_equal(uid, 1000);
@@ -82,7 +73,7 @@ entry_found(void **state)
8273
assert_int_equal(uid, 1000);
8374
assert_int_equal(gid, 1000);
8475

85-
pcmk__mock_getpwnam_r = false;
76+
pcmk__mock_getpwnam = false;
8677
}
8778

8879
PCMK__UNIT_TEST(NULL, NULL,

0 commit comments

Comments
 (0)