Skip to content

Commit c61c251

Browse files
committed
WIP: Add a process manager "class"
In many places there's a need to keep track of sub-processes spawned by the main process. Such processes need to be looked up by various parameters, interacted with, and, eventually, terminated. Having a common, reliable and fast implementation with high test coverage that holds the various pieces of information about sub-processes together is beneficial. Ticket: CFE-3572 Changelog: None
1 parent 17aad59 commit c61c251

File tree

5 files changed

+869
-1
lines changed

5 files changed

+869
-1
lines changed

libutils/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ libutils_la_SOURCES = \
6363
platform.h condition_macros.h \
6464
printsize.h \
6565
proc_keyvalue.c proc_keyvalue.h \
66+
proc_manager.c proc_manager.h \
6667
queue.c queue.h \
6768
rb-tree.c rb-tree.h \
6869
refcount.c refcount.h \

libutils/proc_manager.c

Lines changed: 296 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
1+
/*
2+
Copyright 2021 Northern.tech AS
3+
4+
This file is part of CFEngine 3 - written and maintained by Northern.tech AS.
5+
6+
This program is free software; you can redistribute it and/or modify it
7+
under the terms of the GNU General Public License as published by the
8+
Free Software Foundation; version 3.
9+
10+
This program is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
GNU General Public License for more details.
14+
15+
You should have received a copy of the GNU General Public License
16+
along with this program; if not, write to the Free Software
17+
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
18+
19+
To the extent this program is licensed as part of the Enterprise
20+
versions of CFEngine, the applicable Commercial Open Source License
21+
(COSL) may apply to this file if you as a licensee so wish it. See
22+
included file COSL.txt.
23+
*/
24+
25+
#include <platform.h>
26+
#include <map.h> /* Map* */
27+
#include <alloc.h> /* xmalloc, xasprintf */
28+
#include <string_lib.c> /* StringHash_untyped, StringEqual_untyped */
29+
#include <logging.h>
30+
#include <proc_manager.h>
31+
32+
#ifdef __MINGW32__
33+
#define fileno _fileno
34+
#endif
35+
36+
struct ProcManager_ {
37+
Map *procs_by_id; /** primary storage for the subprocesses info (owns the data) */
38+
Map *procs_by_fd; /** a different view on the data to speed up lookup by FD/stream */
39+
Map *procs_by_pid; /** a different view on the data to speed up lookup by PID */
40+
};
41+
42+
static unsigned int HashFD_untyped(const void *fd, ARG_UNUSED unsigned int seed)
43+
{
44+
assert(fd != NULL);
45+
return *((unsigned int*) fd);
46+
}
47+
48+
static bool FDEqual_untyped(const void *fd1, const void *fd2)
49+
{
50+
assert((fd1 != NULL) && (fd2 != NULL));
51+
return (*((int*) fd1) == (*(int*) fd2));
52+
}
53+
54+
static unsigned int HashPID_untyped(const void *pid, ARG_UNUSED unsigned int seed)
55+
{
56+
assert(pid != NULL);
57+
return *((unsigned int*) pid);
58+
}
59+
60+
static bool PIDEqual_untyped(const void *pid1, const void *pid2)
61+
{
62+
assert((pid1 != NULL) && (pid2 != NULL));
63+
return (*((pid_t*) pid1) == *((pid_t*) pid2));
64+
}
65+
66+
static void NoopDestroy(ARG_UNUSED void *p)
67+
{
68+
/* not destroying anything here */
69+
}
70+
71+
ProcManager *ProcManagerNew()
72+
{
73+
ProcManager *manager = xmalloc(sizeof(ProcManager));
74+
75+
/* keys are just pointers into data, this map actually holds the data */
76+
manager->procs_by_id = MapNew(StringHash_untyped, StringEqual_untyped,
77+
NoopDestroy, (MapDestroyDataFn) SubprocessDestroy);
78+
79+
/* keys are allocated, data belongs to the procs_by_id map */
80+
manager->procs_by_fd = MapNew(HashFD_untyped, FDEqual_untyped,
81+
free, NoopDestroy);
82+
83+
/* keys are just pointers into data, data belongs to the procs_by_id map */
84+
manager->procs_by_pid = MapNew(HashPID_untyped, PIDEqual_untyped,
85+
NoopDestroy, NoopDestroy);
86+
87+
return manager;
88+
}
89+
90+
void ProcManagerDestroy(ProcManager *manager)
91+
{
92+
if (manager != NULL)
93+
{
94+
/* procs_by_id owns the data */
95+
MapSoftDestroy(manager->procs_by_fd);
96+
MapSoftDestroy(manager->procs_by_pid);
97+
MapDestroy(manager->procs_by_id);
98+
free(manager);
99+
}
100+
}
101+
102+
static inline Subprocess *SubprocessNew(char *id, char *cmd, char *description,
103+
pid_t pid, FILE *input, FILE *output, char lookup_io)
104+
{
105+
Subprocess *ret = xmalloc(sizeof(Subprocess));
106+
ret->id = id;
107+
ret->cmd = cmd;
108+
ret->description = description;
109+
ret->pid = pid;
110+
ret->input = input;
111+
ret->output = output;
112+
ret->lookup_io = lookup_io;
113+
114+
return ret;
115+
}
116+
117+
void SubprocessDestroy(Subprocess *proc)
118+
{
119+
if (proc != NULL)
120+
{
121+
free(proc->id);
122+
free(proc->cmd);
123+
free(proc->description);
124+
free(proc);
125+
}
126+
}
127+
128+
static inline int *DupInt(int val)
129+
{
130+
int *ret = xmalloc(sizeof(int));
131+
*ret = val;
132+
return ret;
133+
}
134+
135+
bool ProcManagerAddProcess(ProcManager *manager,
136+
char *id, char *cmd, char *description, pid_t pid,
137+
FILE *input, FILE *output, char lookup_io)
138+
{
139+
assert(manager != NULL);
140+
141+
if (id == NULL)
142+
{
143+
NDEBUG_UNUSED int ret = xasprintf(&id, "%jd", (intmax_t) pid);
144+
assert(ret > 0);
145+
}
146+
147+
int *lookup_fd = NULL;
148+
if (lookup_io == 'i')
149+
{
150+
assert(input != NULL);
151+
int fd = fileno(input);
152+
if (fd >= 0)
153+
{
154+
lookup_fd = DupInt(fd);
155+
}
156+
else
157+
{
158+
Log(LOG_LEVEL_ERR, "Failed to get FD for input file stream for the process '%s'",
159+
description != NULL ? description : id);
160+
}
161+
}
162+
else if (lookup_io == 'o')
163+
{
164+
assert(output != NULL);
165+
int fd = fileno(output);
166+
if (fd >= 0)
167+
{
168+
lookup_fd = DupInt(fd);
169+
}
170+
else
171+
{
172+
Log(LOG_LEVEL_ERR, "Failed to get FD for output file stream for the process '%s'",
173+
description != NULL ? description : id);
174+
}
175+
}
176+
else
177+
{
178+
assert(lookup_io == '\0');
179+
}
180+
181+
if (MapHasKey(manager->procs_by_id, id) ||
182+
MapHasKey(manager->procs_by_pid, &pid) ||
183+
((lookup_fd != NULL) && MapHasKey(manager->procs_by_fd, lookup_fd)))
184+
{
185+
Log(LOG_LEVEL_ERR,
186+
"Attempt to insert the process '%s:%jd:%d' twice into the same process manager",
187+
id, (intmax_t) pid, *lookup_fd);
188+
189+
free(id);
190+
free(cmd);
191+
free(description);
192+
free(lookup_fd);
193+
194+
return false;
195+
}
196+
197+
Subprocess *proc = SubprocessNew(id, cmd, description, pid, input, output, lookup_io);
198+
MapInsert(manager->procs_by_id, id, proc);
199+
MapInsert(manager->procs_by_pid, &(proc->pid), proc);
200+
201+
if (lookup_fd != NULL)
202+
{
203+
MapInsert(manager->procs_by_fd, lookup_fd, proc);
204+
}
205+
206+
return true;
207+
}
208+
209+
210+
Subprocess *ProcManagerGetProcessByPID(ProcManager *manager, pid_t pid)
211+
{
212+
assert(manager != NULL);
213+
return (Subprocess*) MapGet(manager->procs_by_pid, &pid);
214+
}
215+
216+
Subprocess *ProcManagerGetProcessById(ProcManager *manager, const char *id)
217+
{
218+
assert(manager != NULL);
219+
return (Subprocess*) MapGet(manager->procs_by_id, id);
220+
}
221+
222+
Subprocess *ProcManagerGetProcessByFD(ProcManager *manager, int fd)
223+
{
224+
assert(manager != NULL);
225+
return (Subprocess*) MapGet(manager->procs_by_fd, &fd);
226+
}
227+
228+
Subprocess *ProcManagerGetProcessByStream(ProcManager *manager, FILE *stream)
229+
{
230+
assert(manager != NULL);
231+
int fd = fileno(stream);
232+
return (Subprocess*) MapGet(manager->procs_by_fd, &fd);
233+
}
234+
235+
236+
static inline void SoftRemoveProcFromMaps(ProcManager *manager, Subprocess *proc)
237+
{
238+
assert(manager != NULL);
239+
240+
MapRemoveSoft(manager->procs_by_id, proc->id);
241+
MapRemoveSoft(manager->procs_by_pid, &(proc->pid));
242+
243+
int lookup_fd = -1;
244+
if (proc->lookup_io == 'i')
245+
{
246+
lookup_fd = fileno(proc->input);
247+
assert(lookup_fd != -1);
248+
}
249+
else if (proc->lookup_io == 'o')
250+
{
251+
lookup_fd = fileno(proc->output);
252+
assert(lookup_fd != -1);
253+
}
254+
255+
if (lookup_fd != -1)
256+
{
257+
MapRemoveSoft(manager->procs_by_fd, &lookup_fd);
258+
}
259+
}
260+
261+
Subprocess *ProcManagerPopProcessByPID(ProcManager *manager, pid_t pid)
262+
{
263+
assert(manager != NULL);
264+
265+
Subprocess *proc = MapGet(manager->procs_by_pid, &pid);
266+
SoftRemoveProcFromMaps(manager, proc);
267+
return proc;
268+
}
269+
270+
Subprocess *ProcManagerPopProcessById(ProcManager *manager, const char *id)
271+
{
272+
assert(manager != NULL);
273+
274+
Subprocess *proc = MapGet(manager->procs_by_id, id);
275+
SoftRemoveProcFromMaps(manager, proc);
276+
return proc;
277+
}
278+
279+
Subprocess *ProcManagerPopProcessByFD(ProcManager *manager, int fd)
280+
{
281+
assert(manager != NULL);
282+
283+
Subprocess *proc = MapGet(manager->procs_by_fd, &fd);
284+
SoftRemoveProcFromMaps(manager, proc);
285+
return proc;
286+
}
287+
288+
Subprocess *ProcManagerPopProcessByStream(ProcManager *manager, FILE *stream)
289+
{
290+
assert(manager != NULL);
291+
292+
int fd = fileno(stream);
293+
Subprocess *proc = MapGet(manager->procs_by_fd, &fd);
294+
SoftRemoveProcFromMaps(manager, proc);
295+
return proc;
296+
}

0 commit comments

Comments
 (0)