@cryptotaxi247 / netdata-1 / commits / 587e83601

apps.plugin improvements (#18652)

* apps.plugin now supports simple patterns when an asterisk is in the middle of a match; expanded kernel threads matching to group them into meaningful entities * removed cli tools * systemd merged * apps.plugin now has the option to print the tree with the target assignment * apps.plugin now extracts the full comm name from the cmdline * optimizations * updated windows comm handling * get the full command line on windows * extract service names for svchost.exe processes * get service names from SCM * Update src/collectors/apps.plugin/README.md Co-authored-by: Fotis Voutsas <fotis@netdata.cloud> * Update src/collectors/apps.plugin/README.md Co-authored-by: Fotis Voutsas <fotis@netdata.cloud> * Update src/collectors/apps.plugin/README.md Co-authored-by: Fotis Voutsas <fotis@netdata.cloud> * Update src/collectors/apps.plugin/README.md Co-authored-by: Fotis Voutsas <fotis@netdata.cloud> * Update src/collectors/apps.plugin/README.md Co-authored-by: Fotis Voutsas <fotis@netdata.cloud> * Update src/collectors/apps.plugin/README.md Co-authored-by: Fotis Voutsas <fotis@netdata.cloud> * Update src/collectors/apps.plugin/README.md Co-authored-by: Fotis Voutsas <fotis@netdata.cloud> * Update src/collectors/apps.plugin/README.md Co-authored-by: Fotis Voutsas <fotis@netdata.cloud> * Update src/collectors/apps.plugin/README.md Co-authored-by: Fotis Voutsas <fotis@netdata.cloud> * Update src/collectors/apps.plugin/README.md Co-authored-by: Fotis Voutsas <fotis@netdata.cloud> * fix compilation on freebsd and macos * windows priveleges * add missing opening quote on windows spawn server * fix alerts notifications infinite loop when alarm-notify.sh cannot be executed --------- Co-authored-by: Fotis Voutsas <fotis@netdata.cloud>

Costa Tsaousis committed Oct 2, 2024 at 18:12 UTC 587e8360193c26d0645e7987764d02e13e016d95
18 files changed +802 -300
CMakeLists.txt
+3 -1
@@ -1895,12 +1895,14 @@ if(ENABLE_PLUGIN_APPS)
1895 src/collectors/apps.plugin/apps_os_macos.c
1896 src/collectors/apps.plugin/apps_os_windows.c
1897 src/collectors/apps.plugin/apps_incremental_collection.c
1898 + src/collectors/apps.plugin/apps_os_windows_nt.c
1899 )
1900
1901 add_executable(apps.plugin ${APPS_PLUGIN_FILES})
1902
1903 target_link_libraries(apps.plugin libnetdata ${CAP_LIBRARIES}
1903 - "$<$<BOOL:${OS_WINDOWS}>:Version>")
1904 + "$<$<BOOL:${OS_WINDOWS}>:Version>"
1905 + "$<$<BOOL:${OS_WINDOWS}>:ntdll>")
1906
1907 target_include_directories(apps.plugin PRIVATE ${CAP_INCLUDE_DIRS})
1908 target_compile_options(apps.plugin PRIVATE ${CAP_CFLAGS_OTHER})
src/collectors/apps.plugin/README.md
+38 -27
@@ -132,6 +132,26 @@ its CPU resources will be cut in half, and data collection will be once every 2
132
133 The configuration file is `/etc/netdata/apps_groups.conf`. You can edit this file using our [`edit-config`](docs/netdata-agent/configuration/README.md) script.
134
135 +### Configuring process managers
136 +
137 +`apps.plugin` needs to know the common process managers, meaning the names of the processes
138 +which spawn other processes. Process managers are used so that `apps.plugin` will automatically
139 +consider all their sub-processes important to monitor.
140 +
141 +Process managers are configured in `apps_groups.conf` with the prefix `managers:`, like this:
142 +
143 +```
144 +managers: process1 process2 process3
145 +```
146 +
147 +Multiple lines may exist, all starting with `managers:`.
148 +
149 +The process names given here should be exactly as the operating system sets them. In Linux these
150 +process names are limited to 15 characters. Usually the command `ps -e` or `cat /proc/{PID}/stat`
151 +states the names needed here.
152 +
153 +### Configuring process groups and renaming processes
154 +
155 The configuration file works accepts multiple lines, each having this format:
156
157 ```txt
@@ -140,48 +160,39 @@ group: process1 process2 ...
160
161 Each group can be given multiple times, to add more processes to it.
162
143 -For the **Applications** section, only groups configured in this file are reported.
144 -All other processes will be reported as `other`.
145 -
146 -For each process given, its whole process tree will be grouped, not just the process matched.
147 -The plugin will include both parents and children. If including the parents into the group is
148 -undesirable, the line `other: *` should be appended to the `apps_groups.conf`.
163 +For each process given, all of its sub-processes will be grouped, not just the matched process.
164
165 The process names are the ones returned by:
166
152 -- `ps -e` or `cat /proc/PID/stat`
153 -- in case of substring mode (see below): `/proc/PID/cmdline`
167 +- **comm**: `ps -e` or `cat /proc/{PID}/stat`
168 +- **cmdline**: in case of substring mode (see below): `/proc/{PID}/cmdline`
169 +
170 +On Linux **comm** is limited to just a few characters. `apps.plugin` attempts to find the entire
171 +**comm** name by looking for it at the **cmdline**. When this is successful, the entire process name
172 +is available, otherwise the shortened one is used.
173
174 To add process names with spaces, enclose them in quotes (single or double)
175 example: `'Plex Media Serv'` or `"my other process"`.
176
158 -You can add an asterisk `*` at the beginning and/or the end of a process:
177 +You can add asterisks (`*`) to provide a pattern:
178
160 -- `*name` _suffix_ mode: will search for processes ending with `name` (at `/proc/PID/stat`)
161 -- `name*` _prefix_ mode: will search for processes beginning with `name` (at `/proc/PID/stat`)
162 -- `*name*` _substring_ mode: will search for `name` in the whole command line (at `/proc/PID/cmdline`)
179 +- `*name` _suffix_ mode: will match a **comm** ending with `name`.
180 +- `name*` _prefix_ mode: will match a **comm** beginning with `name`.
181 +- `*name*` _substring_ mode: will search for `name` in **cmdline**.
182
164 -If you enter even just one _name_ (substring), `apps.plugin` will process
165 -`/proc/PID/cmdline` for all processes (of course only once per process: when they are first seen).
183 +Asterisks may appear in the middle of `name` (like `na*me`), without affecting what is being
184 +matched (**comm** or **cmdline**).
185
186 To add processes with single quotes, enclose them in double quotes: `"process with this ' single quote"`
187
188 To add processes with double quotes, enclose them in single quotes: `'process with this " double quote'`
189
171 -If a group or process name starts with a `-`, the dimension will be hidden from the chart (cpu chart only).
172 -
173 -If a process starts with a `+`, debugging will be enabled for it (debugging produces a lot of output - do not enable it in production systems).
174 -
175 -You can add any number of groups. Only the ones found running will affect the charts generated.
176 -However, producing charts with hundreds of dimensions may slow down your web browser.
177 -
178 -The order of the entries in this list is important: the first that matches a process is used, so put important
179 -ones at the top. Processes not matched by any row, will inherit it from their parents or children.
180 -
181 -The order also controls the order of the dimensions on the generated charts (although applications started
182 -after apps.plugin is started, will be appended to the existing list of dimensions the `netdata` daemon maintains).
190 +The order of the entries in this list is important: the first one that matches a process is used, so follow a top-down hierarchy.
191 +Processes not matched by any row, will inherit it from their parents.
192
184 -There are a few command line options you can pass to `apps.plugin`. The list of available options can be acquired with the `--help` flag. The options can be set in the `netdata.conf` file. For example, to disable user and user group charts you should set
193 +There are a few command line options you can pass to `apps.plugin`. The list of available
194 +options can be acquired with the `--help` flag. The options can be set in the `netdata.conf` using the [`edit-config` script](/docs/netdata-agent/configuration/README.md).
195 +For example, to disable user and user group charts you would set:
196
197 ```
198 [plugin:apps]
src/collectors/apps.plugin/apps_aggregations.c
+63 -24
@@ -74,7 +74,7 @@ static inline void aggregate_pid_on_target(struct target *w, struct pid_stat *p,
74 if(!w->uptime_min || p->values[PDF_UPTIME] < w->uptime_min) w->uptime_min = p->values[PDF_UPTIME];
75 if(!w->uptime_max || w->uptime_max < p->values[PDF_UPTIME]) w->uptime_max = p->values[PDF_UPTIME];
76
77 - if(unlikely(debug_enabled || w->debug_enabled)) {
77 + if(unlikely(debug_enabled)) {
78 struct pid_on_target *pid_on_target = mallocz(sizeof(struct pid_on_target));
79 pid_on_target->pid = p->pid;
80 pid_on_target->next = w->root_pid;
@@ -110,27 +110,61 @@ static inline void cleanup_exited_pids(void) {
110 }
111 }
112
113 -static struct target *get_app_group_target_for_pid(struct pid_stat *p) {
113 +static struct target *matched_apps_groups_target(struct pid_stat *p, struct target *w) {
114 + if(is_process_manager(p))
115 + return NULL;
116 +
117 + p->matched_by_config = true;
118 + return w->target ? w->target : w;
119 +}
120 +
121 +static struct target *get_apps_groups_target_for_pid(struct pid_stat *p) {
122 targets_assignment_counter++;
123
124 for(struct target *w = apps_groups_root_target; w ; w = w->next) {
125 if(w->type != TARGET_TYPE_APP_GROUP) continue;
126
119 - // find it - 4 cases:
120 - // 1. the target is not a pattern
121 - // 2. the target has the prefix
122 - // 3. the target has the suffix
123 - // 4. the target is something inside cmdline
124 -
125 - if(unlikely(( (!w->starts_with && !w->ends_with && w->compare == p->comm)
126 - || (w->starts_with && !w->ends_with && string_starts_with_string(p->comm, w->compare))
127 - || (!w->starts_with && w->ends_with && string_ends_with_string(p->comm, w->compare))
128 - || (proc_pid_cmdline_is_needed && w->starts_with && w->ends_with && strstr(pid_stat_cmdline(p), string2str(w->compare)))
129 - ))) {
130 -
131 - p->matched_by_config = true;
132 - if(w->target) return w->target;
133 - else return w;
127 + if(!w->starts_with && !w->ends_with) {
128 + if(w->ag.pattern) {
129 + if(simple_pattern_matches_string(w->ag.pattern, p->comm))
130 + return matched_apps_groups_target(p, w);
131 + }
132 + else {
133 + if(w->ag.compare == p->comm || w->ag.compare == p->comm_orig)
134 + return matched_apps_groups_target(p, w);
135 + }
136 + }
137 + else if(w->starts_with && !w->ends_with) {
138 + if(w->ag.pattern) {
139 + if(simple_pattern_matches_string(w->ag.pattern, p->comm))
140 + return matched_apps_groups_target(p, w);
141 + }
142 + else {
143 + if(string_starts_with_string(p->comm, w->ag.compare) ||
144 + (p->comm != p->comm_orig && string_starts_with_string(p->comm, w->ag.compare)))
145 + return matched_apps_groups_target(p, w);
146 + }
147 + }
148 + else if(!w->starts_with && w->ends_with) {
149 + if(w->ag.pattern) {
150 + if(simple_pattern_matches_string(w->ag.pattern, p->comm))
151 + return matched_apps_groups_target(p, w);
152 + }
153 + else {
154 + if(string_ends_with_string(p->comm, w->ag.compare) ||
155 + (p->comm != p->comm_orig && string_ends_with_string(p->comm, w->ag.compare)))
156 + return matched_apps_groups_target(p, w);
157 + }
158 + }
159 + else if(w->starts_with && w->ends_with && p->cmdline) {
160 + if(w->ag.pattern) {
161 + if(simple_pattern_matches_string(w->ag.pattern, p->cmdline))
162 + return matched_apps_groups_target(p, w);
163 + }
164 + else {
165 + if(strstr(string2str(p->cmdline), string2str(w->ag.compare)))
166 + return matched_apps_groups_target(p, w);
167 + }
168 }
169 }
170
@@ -141,19 +175,23 @@ static void assign_a_target_to_all_processes(void) {
175 // assign targets from app_groups.conf
176 for(struct pid_stat *p = root_of_pids(); p ; p = p->next) {
177 if(!p->target)
144 - p->target = get_app_group_target_for_pid(p);
178 + p->target = get_apps_groups_target_for_pid(p);
179 }
180
181 // assign targets from their parents, if they have
182 for(struct pid_stat *p = root_of_pids(); p ; p = p->next) {
183 if(!p->target) {
150 - for(struct pid_stat *pp = p->parent ; pp ; pp = pp->parent) {
151 - if(pp->target) {
152 - if(pp->matched_by_config) {
153 - // we are only interested about app_groups.conf matches
154 - p->target = pp->target;
184 + if(!p->is_manager) {
185 + for (struct pid_stat *pp = p->parent; pp; pp = pp->parent) {
186 + if(pp->is_manager) break;
187 +
188 + if (pp->target) {
189 + if (pp->matched_by_config) {
190 + // we are only interested about app_groups.conf matches
191 + p->target = pp->target;
192 + }
193 + break;
194 }
156 - break;
195 }
196 }
197
@@ -180,6 +218,7 @@ void aggregate_processes_to_targets(void) {
218
219 // this has to be done, before the cleanup
220 struct target *w = NULL, *o = NULL;
221 + (void)w; (void)o;
222
223 // concentrate everything on the targets
224 for(struct pid_stat *p = root_of_pids(); p ; p = p->next) {
src/collectors/apps.plugin/apps_groups.conf
+150 -32
@@ -4,19 +4,21 @@
4 ## Documentation at:
5 ## https://github.com/netdata/netdata/blob/master/src/collectors/apps.plugin/README.md
6 ##
7 -## The list of process managers can be configured here (uncomment and edit):
7 +## Subprocesses of process managers are monitored.
8 +## (uncomment to edit - the default is also hardcoded into the plugin)
9
9 -## Linux
10 -#managers: init systemd containerd-shim dumb-init gnome-shell docker-init
10 +## Linux process managers
11 +#managers: init systemd containerd-shim-runc-v2 dumb-init gnome-shell docker-init
12 +#managers: openrc-run.sh crond plasmashell xfwm4
13
12 -## FreeBSD
14 +## FreeBSD process managers
15 #managers: init
16
15 -## MacOS
17 +## MacOS process managers
18 #managers: launchd
19
18 -## Windows
19 -#managers: System services wininit
20 +## Windows process managers
21 +#managers: wininit services explorer System
22
23 ## -----------------------------------------------------------------------------
24 ## Processes of interest
@@ -26,23 +28,23 @@ netdata: netdata
28 ## netdata known plugins
29 ## plugins not defined here will be accumulated into netdata, above
30 apps.plugin: *apps.plugin*
29 -freeipmi.plugin: *freeipmi.plugin*
30 -nfacct.plugin: *nfacct.plugin*
31 +go.d.plugin: *go.d.plugin*
32 +systemd-journal.plugin: *systemd-journal.plugin*
33 +network-viewer.plugin: *network-viewer.plugin*
34 +windows-events.plugin: *windows-events.plugin*
35 cups.plugin: *cups.plugin*
32 -xenstat.plugin: *xenstat.plugin*
36 perf.plugin: *perf.plugin*
37 +nfacct.plugin: *nfacct.plugin*
38 +xenstat.plugin: *xenstat.plugin*
39 +freeipmi.plugin: *freeipmi.plugin*
40 charts.d.plugin: *charts.d.plugin*
41 python.d.plugin: *python.d.plugin*
36 -systemd-journal.plugin: *systemd-journal.plugin*
37 -network-viewer.plugin: *network-viewer.plugin*
38 -windows-events.plugin: *windows-events.plugin*
39 -tc-qos-helper: *tc-qos-helper.sh*
40 -fping: fping
41 -ioping: ioping
42 -go.d.plugin: *go.d.plugin*
42 slabinfo.plugin: *slabinfo.plugin*
43 ebpf.plugin: *ebpf.plugin*
44 debugfs.plugin: *debugfs.plugin*
45 +tc-qos-helper: *tc-qos-helper.sh*
46 +fping: fping
47 +ioping: ioping
48
49 ## agent-service-discovery
50 agent_sd: agent_sd
@@ -65,32 +67,26 @@ azure: mdsd *waagent* *omiserver* *omiagent* hv_kvp_daemon hv_vss_daemon *auoms*
67 datadog: *datadog*
68 newrelic: newrelic*
69 google-agent: *google_guest_agent* *google_osconfig_agent*
68 -ceph: ceph-* ceph_* radosgw* rbd-* cephfs-* osdmaptool crushtool
69 -samba: smbd nmbd winbindd ctdbd ctdb-* ctdb_*
70 -nfs: rpcbind rpc.* nfs*
71 -zfs: spl_* z_* txg_* zil_* arc_* l2arc*
72 -iscsi: iscsid iscsi_eh
73 -afp: netatalk afpd cnid_dbd cnid_metad
70 aws-s3: '*aws s3*' s3cmd s5cmd
71 proxmox-ve: pve* spiceproxy
72 libvirt: virtlogd virtqemud virtstoraged virtnetworkd virtlockd virtinterfaced
73 libvirt: virtnodedevd virtproxyd virtsecretd libvirtd
74 guest-agent: qemu-ga spice-vdagent cloud-init*
79 -dhcp: *dhcp* dhclient
75 +dhcp: dhcp* dhclient
76
77 build: cc1 cc1plus as gcc* cppcheck ld make cmake automake autoconf autoreconf
78 build: cargo rustc bazel buck git gdb valgrind* rpmbuild dpkg-buildpackage
83 -packagemanager: apt* dpkg* dselect dnf yum rpm zypp* yast* pacman xbps* swupd* emerge*
84 -packagemanager: packagekitd pkgin pkg apk snapd slackpkg slapt-get
79 +packagemanager: apt* dpkg* dselect dnf yum rpm zypp* yast* pacman xbps* swupd*
80 +packagemanager: packagekitd pkgin pkg apk snapd slackpkg slapt-get emerge*
81 clam: clam* *clam
82 backup: rsync lsyncd bacula* borg rclone
83 cron: cron* atd anacron *systemd-cron* incrond
84 ups: upsmon upsd */nut/* apcupsd
89 -audio: pulse* pipewire wireplumber jack*
85
86 rabbitmq: *rabbitmq*
87 sidekiq: *sidekiq*
88 erlang: beam.smp
89 +postfix: *postfix*
90
91 ## -----------------------------------------------------------------------------
92 ## java applications
@@ -117,12 +113,134 @@ kafka: *kafka.Kafka*
113
114 ## -----------------------------------------------------------------------------
115 ## Kernel / System
116 +## The following are interesting kernel threads and related processes to
117 +## monitor individually, mainly for their CPU utilization.
118 +
119 +## These kernel threads switch tasks all the time, so they should never be
120 +## categorized as anything specific.
121 +kernel: kworker/*
122
123 +## Kernel Samepage Merging (KSM) daemon that looks for identical memory pages
124 +## across processes and merges them to save memory.
125 ksmd: ksmd
122 -khugepaged: khugepaged
126 +
127 +## Handles migration of processes between CPU cores to balance load.
128 +kmigration: migration/*
129 +
130 +## Manages memory compaction, moving memory pages around to reduce
131 +## fragmentation.
132 +kcompactd: kcompactd*
133 +
134 +## Responsible for freeing up memory by swapping pages to disk when needed.
135 +kswapd: kswapd*
136 +
137 +## DAMON is a mechanism designed to efficiently monitor the memory access
138 +## patterns of running processes or the system itself.
139 kdamond: kdamond
124 -kswapd: kswapd
125 -zswap: zswap
126 -kcompactd: kcompactd
127 -ipvs: ipvs_*
140 +
141 +## Manages ballooning in virtualized environments.
142 +vballoon: vballoon*
143 +
144 +## virtio - Handles or I/O (storage and network) on virtual machines.
145 +kvirtio: virtio-* vhost-*
146 +
147 +## Layer 4 (transport layer) load balancing
148 +ipvs: ipvsd ipvs_* ip_vs_*
149 +
150 +## Hugepages
151 +## Scans memory regions and tries to promote regular-sized pages (4KB) into
152 +## hugepages (2MB) where possible. Merge smaller contiguous 4KB pages into 2MB
153 +## pages. Hugepages also use: kswapd, kcompactd, and migration.
154 +khugepaged: khugepaged
155 +
156 +## Note about zswap:
157 +## zswap does not introduce its own dedicated kernel threads. Instead, it
158 +## operates within the existing memory management and swapping framework of the
159 +## kernel:
160 +## - kswapd: swaps pages in/out of memory, using compression in the process.
161 +## - kcompactd: compacts memory when pages are compressed or moved around.
162 +
163 +## -----------------------------------------------------------------------------
164 +## Block Devices
165 +
166 +## Handles deferred block I/O operations for block devices.
167 +kblockd: kblockd
168 +
169 +## Device Mapper (DM)
170 +device-mapper: kcopyd/* kcryptd/* kdmflush/* dm_bufio_cache
171 +device-mapper: raid1/* raid5/* raid10/* multipathd bioset/*
172 +
173 +## Software RAID (MD)
174 +md-raid: md*_raid* md*_resync md*_reshape md*_recovery md_thread
175 +md-raid: flush_md* raid*_sync
176 +
177 +## iSCSI
178 +iscsi: iscsid iscsiadm iscsi_eh/* iscsi_xmit/* iscsi_ttx/* iscsi_rx/* iscsi_trx/*
179 +
180 +## SCSI
181 +scsi: scsi_eh/* scsi_tmf/* scsi_wq/*
182 +
183 +## BCACHE
184 +bcache: bcache* bch_btree_io bch_journal
185 +
186 +## SAS
187 +sas: sas_task/* mpt*
188 +
189 +## Fibre Channel (FC)
190 +fc: fc_transport qla2xxx*
191 +
192 +## loop devices
193 +loop: loop* flush-loop*
194 +
195 +## -----------------------------------------------------------------------------
196 +## Filesystems
197 +
198 +## Ext4
199 +ext4: ext4-* jbd2/*
200 +
201 +## XFS
202 +xfs: xfs*
203 +
204 +## BTRFS
205 btrfs: btrfs*
206 +
207 +## NFS
208 +nfs: rpcbind rpc.* nfs* rpciod
209 +
210 +## ZFS
211 +zfs: spl_* z_* txg_* zil_* arc_* l2arc* zfs* zed zdb zpool*
212 +
213 +## CEPH
214 +ceph: ceph-* ceph_* radosgw* rbd-* cephfs-*
215 +ceph: ceph cephadm osdmaptool crushtool rados rbd
216 +
217 +## CIFS & Samba
218 +cifs: smbd nmbd winbindd ctdbd ctdb-* ctdb_*
219 +cifs: cifsd cifscreds cifs.upcall
220 +
221 +## Apple Filling Protocol (AFP)
222 +afp: netatalk afpd cnid_dbd cnid_metad
223 +
224 +## -----------------------------------------------------------------------------
225 +## Desktops
226 +
227 +systemd-journald: *systemd-journal*
228 +systemd: systemd systemd-*
229 +
230 +## GNOME
231 +desktop: gnome-* gsd-* gjs goa-* gcr-* gvfs-* *xdg-*-gnome* passimd gvfsd*
232 +desktop: at-spi-* at-spi2-* dconf-service gcr-*
233 +
234 +## KDE
235 +desktop: plasmashell kwin-* kde* *-kde-* klauncher kactivitymanagerd krunner
236 +desktop: kdeconnectd ksmserver kglobalaccel5 plasma-* *org.kde.*
237 +desktop: sddm* kwalletd5 knotify5 kmix kscreen kwayland-*
238 +
239 +## XFCE4
240 +desktop: xfce4-* xfwm4 xfdesktop xfce4-panel xfsettingsd xfconfd
241 +desktop: lightdm lightdm-*
242 +
243 +## Generic tools related to desktop
244 +desktop: gdm gdm-* dbus-* xdg-* ibus-* evolution-* accounts-daemon colord
245 +desktop: geoclue pulse* pipewire* wireplumber jack* touchegg pulseaudio
246 +desktop: Xwayland Xorg
src/collectors/apps.plugin/apps_os_freebsd.c
+1 -1
@@ -291,7 +291,7 @@ bool apps_os_read_pid_stat_freebsd(struct pid_stat *p, void *ptr) {
291 usec_t started_ut = timeval_usec(&proc_info->ki_start);
292 p->values[PDF_UPTIME] = (system_current_time_ut > started_ut) ? (system_current_time_ut - started_ut) / USEC_PER_SEC : 0;
293
294 - if(unlikely(debug_enabled || (p->target && p->target->debug_enabled)))
294 + if(unlikely(debug_enabled || p->target))
295 debug_log_int("READ PROC/PID/STAT: %s/proc/%d/stat, process: '%s' on target '%s' (dt=%llu) VALUES: utime=" KERNEL_UINT_FORMAT ", stime=" KERNEL_UINT_FORMAT ", cutime=" KERNEL_UINT_FORMAT ", cstime=" KERNEL_UINT_FORMAT ", minflt=" KERNEL_UINT_FORMAT ", majflt=" KERNEL_UINT_FORMAT ", cminflt=" KERNEL_UINT_FORMAT ", cmajflt=" KERNEL_UINT_FORMAT ", threads=%d",
296 netdata_configured_host_prefix, p->pid, pid_stat_comm(p), (p->target)?string2str(p->target->name):"UNSET",
297 p->stat_collected_usec - p->last_stat_collected_usec,
src/collectors/apps.plugin/apps_os_linux.c
+2 -2
@@ -93,7 +93,7 @@ bool apps_os_read_pid_fds_linux(struct pid_stat *p, void *ptr __maybe_unused) {
93 if(unlikely(l == -1)) {
94 // cannot read the link
95
96 - if(debug_enabled || (p->target && p->target->debug_enabled))
96 + if(debug_enabled)
97 netdata_log_error("Cannot read link %s", p->fds[fdid].filename);
98
99 if(unlikely(p->fds[fdid].fd < 0)) {
@@ -689,7 +689,7 @@ bool apps_os_read_pid_stat_linux(struct pid_stat *p, void *ptr __maybe_unused) {
689 }
690 }
691
692 - if(unlikely(debug_enabled || (p->target && p->target->debug_enabled)))
692 + if(unlikely(debug_enabled))
693 debug_log_int("READ PROC/PID/STAT: %s/proc/%d/stat, process: '%s' on target '%s' (dt=%llu) VALUES: utime=" KERNEL_UINT_FORMAT ", stime=" KERNEL_UINT_FORMAT ", cutime=" KERNEL_UINT_FORMAT ", cstime=" KERNEL_UINT_FORMAT ", minflt=" KERNEL_UINT_FORMAT ", majflt=" KERNEL_UINT_FORMAT ", cminflt=" KERNEL_UINT_FORMAT ", cmajflt=" KERNEL_UINT_FORMAT ", threads=" KERNEL_UINT_FORMAT,
694 netdata_configured_host_prefix, p->pid, pid_stat_comm(p), (p->target)?string2str(p->target->name):"UNSET", p->stat_collected_usec - p->last_stat_collected_usec,
695 p->values[PDF_UTIME],
src/collectors/apps.plugin/apps_os_macos.c
+1 -1
@@ -242,7 +242,7 @@ bool apps_os_read_pid_stat_macos(struct pid_stat *p, void *ptr) {
242 // Note: Some values such as guest time, cutime, cstime, etc., are not directly available in MacOS.
243 // You might need to approximate or leave them unset depending on your needs.
244
245 - if(unlikely(debug_enabled || (p->target && p->target->debug_enabled))) {
245 + if(unlikely(debug_enabled || p->target)) {
246 debug_log_int("READ PROC/PID/STAT for MacOS: process: '%s' on target '%s' VALUES: utime=" KERNEL_UINT_FORMAT ", stime=" KERNEL_UINT_FORMAT ", minflt=" KERNEL_UINT_FORMAT ", majflt=" KERNEL_UINT_FORMAT ", threads=%d",
247 pid_stat_comm(p), (p->target) ? string2str(p->target->name) : "UNSET",
248 p->values[PDF_UTIME],
src/collectors/apps.plugin/apps_os_windows.c
+208 -81
@@ -451,6 +451,8 @@
451 #include <tchar.h>
452 #include <strsafe.h>
453
454 +WCHAR* GetProcessCommandLine(HANDLE hProcess);
455 +
456 struct perflib_data {
457 PERF_DATA_BLOCK *pDataBlock;
458 PERF_OBJECT_TYPE *pObjectType;
@@ -458,34 +460,17 @@ struct perflib_data {
460 DWORD pid;
461 };
462
461 -BOOL EnableDebugPrivilege() {
462 - HANDLE hToken;
463 - LUID luid;
464 - TOKEN_PRIVILEGES tkp;
465 -
466 - if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
467 - return FALSE;
468 -
469 - if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid))
470 - return FALSE;
471 -
472 - tkp.PrivilegeCount = 1;
473 - tkp.Privileges[0].Luid = luid;
474 - tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
475 -
476 - if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL))
477 - return FALSE;
478 -
479 - CloseHandle(hToken);
480 -
481 - return TRUE;
482 -}
483 -
463 void apps_os_init_windows(void) {
464 PerflibNamesRegistryInitialize();
465
487 - if(!EnableDebugPrivilege())
488 - nd_log(NDLS_COLLECTORS, NDLP_WARNING, "Failed to enable debug privilege");
466 + if(!EnableWindowsPrivilege(SE_DEBUG_NAME))
467 + nd_log(NDLS_COLLECTORS, NDLP_WARNING, "Failed to enable %s privilege", SE_DEBUG_NAME);
468 +
469 + if(!EnableWindowsPrivilege(SE_SYSTEM_PROFILE_NAME))
470 + nd_log(NDLS_COLLECTORS, NDLP_WARNING, "Failed to enable %s privilege", SE_SYSTEM_PROFILE_NAME);
471 +
472 + if(!EnableWindowsPrivilege(SE_PROF_SINGLE_PROCESS_NAME))
473 + nd_log(NDLS_COLLECTORS, NDLP_WARNING, "Failed to enable %s privilege", SE_PROF_SINGLE_PROCESS_NAME);
474 }
475
476 uint64_t apps_os_get_total_memory_windows(void) {
@@ -500,10 +485,31 @@ uint64_t apps_os_get_total_memory_windows(void) {
485 return memStat.ullTotalPhys;
486 }
487
503 -static __thread wchar_t unicode[PATH_MAX];
488 +// remove the PID suffix and .exe suffix, if any
489 +static void fix_windows_comm(struct pid_stat *p, char *comm) {
490 + char pid[UINT64_MAX_LENGTH + 1]; // +1 for the underscore
491 + pid[0] = '_';
492 + print_uint64(&pid[1], p->pid);
493 + size_t pid_len = strlen(pid);
494 + size_t comm_len = strlen(comm);
495 + if (pid_len < comm_len) {
496 + char *compare = &comm[comm_len - pid_len];
497 + if (strcmp(pid, compare) == 0)
498 + *compare = '\0';
499 + }
500 +
501 + // remove the .exe suffix, if any
502 + comm_len = strlen(comm);
503 + size_t exe_len = strlen(".exe");
504 + if(exe_len < comm_len) {
505 + char *compare = &comm[comm_len - exe_len];
506 + if (strcmp(".exe", compare) == 0)
507 + *compare = '\0';
508 + }
509 +}
510
511 // Convert wide string to UTF-8
506 -static STRING *wchar_to_string(WCHAR *s) {
512 +static char *wchar_to_utf8(WCHAR *s) {
513 static __thread char utf8[PATH_MAX];
514 static __thread int utf8_size = sizeof(utf8);
515
@@ -512,33 +518,152 @@ static STRING *wchar_to_string(WCHAR *s) {
518 return NULL;
519
520 WideCharToMultiByte(CP_UTF8, 0, s, -1, utf8, utf8_size, NULL, NULL);
515 - return string_strdupz(utf8);
521 + return utf8;
522 }
523
518 -STRING *GetProcessFriendlyName(WCHAR *path) {
524 +// Convert wide string to UTF-8
525 +static STRING *wchar_to_string(WCHAR *s) {
526 + return string_strdupz(wchar_to_utf8(s));
527 +}
528 +
529 +// --------------------------------------------------------------------------------------------------------------------
530 +
531 +// return a sanitized name for the process
532 +STRING *GetProcessFriendlyNameSanitized(WCHAR *path) {
533 static __thread uint8_t void_buf[1024 * 1024];
534 + static __thread DWORD void_buf_size = sizeof(void_buf);
535 + static __thread wchar_t unicode[PATH_MAX];
536 + static __thread DWORD unicode_size = sizeof(unicode) / sizeof(*unicode);
537
538 DWORD handle;
539 DWORD size = GetFileVersionInfoSizeW(path, &handle);
523 - if (size == 0 || size > sizeof(void_buf))
540 + if (size == 0 || size > void_buf_size)
541 return FALSE;
542
543 if (GetFileVersionInfoW(path, handle, size, void_buf)) {
544 LPWSTR value = NULL;
545 UINT len = 0;
529 - DWORD unicode_size = sizeof(unicode) / sizeof(*unicode);
546 if (VerQueryValueW(void_buf, L"\\StringFileInfo\\040904B0\\FileDescription", (LPVOID*)&value, &len) &&
547 len > 0 && len < unicode_size) {
548 wcsncpy(unicode, value, unicode_size - 1);
549 unicode[unicode_size - 1] = L'\0';
534 - return wchar_to_string(unicode);
550 + char *name = wchar_to_utf8(unicode);
551 + sanitize_chart_meta(name);
552 + return string_strdupz(name);
553 + }
554 + }
555 +
556 + return NULL;
557 +}
558 +
559 +#define SERVICE_PREFIX "Service "
560 +// return a sanitized name for the process
561 +static STRING *GetNameFromCmdlineSanitized(struct pid_stat *p) {
562 + if(!p->cmdline) return NULL;
563 +
564 + char buf[string_strlen(p->cmdline) + 1];
565 + memcpy(buf, string2str(p->cmdline), sizeof(buf));
566 + char *words[100];
567 + size_t num_words = quoted_strings_splitter(buf, words, 100, isspace_map_pluginsd);
568 +
569 + if(string_strcmp(p->comm, "svchost") == 0) {
570 + // find -s SERVICE in the command line
571 + for(size_t i = 0; i < num_words ;i++) {
572 + if(strcmp(words[i], "-s") == 0 && i + 1 < num_words) {
573 + char service[strlen(words[i + 1]) + sizeof(SERVICE_PREFIX)]; // sizeof() includes a null
574 + strcpy(service, SERVICE_PREFIX);
575 + strcpy(&service[sizeof(SERVICE_PREFIX) - 1], words[i + 1]);
576 + sanitize_chart_meta(service);
577 + return string_strdupz(service);
578 + }
579 }
580 }
581
582 return NULL;
583 }
584
585 +static void GetServiceNames(void) {
586 + SC_HANDLE hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE);
587 + if (hSCManager == NULL) return;
588 +
589 + DWORD dwBytesNeeded = 0, dwServicesReturned = 0, dwResumeHandle = 0;
590 + ENUM_SERVICE_STATUS_PROCESS *pServiceStatus = NULL;
591 +
592 + // First, query the required buffer size
593 + EnumServicesStatusEx(
594 + hSCManager, SC_ENUM_PROCESS_INFO, SERVICE_WIN32, SERVICE_STATE_ALL,
595 + NULL, 0, &dwBytesNeeded, &dwServicesReturned, &dwResumeHandle, NULL);
596 +
597 + if (dwBytesNeeded == 0) {
598 + CloseServiceHandle(hSCManager);
599 + return;
600 + }
601 +
602 + // Allocate memory to hold the services
603 + pServiceStatus = mallocz(dwBytesNeeded);
604 +
605 + // Now, retrieve the list of services
606 + if (!EnumServicesStatusEx(
607 + hSCManager, SC_ENUM_PROCESS_INFO, SERVICE_WIN32, SERVICE_STATE_ALL,
608 + (LPBYTE)pServiceStatus, dwBytesNeeded, &dwBytesNeeded, &dwServicesReturned,
609 + &dwResumeHandle, NULL)) {
610 + freez(pServiceStatus);
611 + CloseServiceHandle(hSCManager);
612 + return;
613 + }
614 +
615 + // Loop through the services
616 + for (DWORD i = 0; i < dwServicesReturned; i++) {
617 + if(!pServiceStatus[i].lpDisplayName || !*pServiceStatus[i].lpDisplayName)
618 + continue;
619 +
620 + struct pid_stat *p = find_pid_entry((pid_t)pServiceStatus[i].ServiceStatusProcess.dwProcessId);
621 + if(p && !p->got_service) {
622 + p->got_service = true;
623 +
624 + size_t len = strlen(pServiceStatus[i].lpDisplayName);
625 + char buf[len + 1];
626 + memcpy(buf, pServiceStatus[i].lpDisplayName, sizeof(buf));
627 + sanitize_chart_meta(buf);
628 +
629 + string_freez(p->name);
630 + p->name = string_strdupz(buf);
631 + }
632 + }
633 +
634 + free(pServiceStatus);
635 + CloseServiceHandle(hSCManager);
636 +}
637 +
638 +static WCHAR *executable_path_from_cmdline(WCHAR *cmdline) {
639 + if (!cmdline || !*cmdline) return NULL;
640 +
641 + WCHAR *exe_path_start = cmdline;
642 + WCHAR *exe_path_end = NULL;
643 +
644 + if (cmdline[0] == L'"') {
645 + // Command line starts with a double quote
646 + exe_path_start++; // Move past the first double quote
647 + exe_path_end = wcschr(exe_path_start, L'"'); // Find the next quote
648 + }
649 + else {
650 + // Command line does not start with a double quote
651 + exe_path_end = wcschr(exe_path_start, L' '); // Find the first space
652 + }
653 +
654 + if (exe_path_end) {
655 + // Null-terminate the string at the end of the executable path
656 + *exe_path_end = L'\0';
657 + return exe_path_start;
658 + }
659 +
660 + return NULL;
661 +}
662 +
663 void GetAllProcessesInfo(void) {
664 + static __thread wchar_t unicode[PATH_MAX];
665 + static __thread DWORD unicode_size = sizeof(unicode) / sizeof(*unicode);
666 +
667 calls_counter++;
668
669 HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
@@ -552,45 +677,70 @@ void GetAllProcessesInfo(void) {
677 return;
678 }
679
680 + bool need_service_names = false;
681 +
682 do {
683 + if(!pe32.th32ProcessID) continue;
684 +
685 struct pid_stat *p = get_or_allocate_pid_entry((pid_t)pe32.th32ProcessID);
686 p->ppid = (pid_t)pe32.th32ParentProcessID;
687 if(p->got_info) continue;
688 p->got_info = true;
689
561 - if(!p->initialized) {
562 - string_freez(p->comm);
563 - p->comm = wchar_to_string(pe32.szExeFile);
564 - p->assigned_to_target = false;
565 - }
566 -
690 HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, p->pid);
568 - if (hProcess == NULL) continue;
691 + if (hProcess == NULL)
692 + continue;
693
570 - STRING *full_path = NULL;
571 - STRING *friendly_name = NULL;
694 + // Get the full command line, if possible
695 + {
696 + WCHAR *cmdline = GetProcessCommandLine(hProcess); // returns malloc'd buffer
697 + if (cmdline) {
698 + string_freez(p->cmdline);
699 + p->cmdline = wchar_to_string(cmdline);
700 +
701 + // extract the process full path from the command line
702 + WCHAR *path = executable_path_from_cmdline(cmdline);
703 + if(path) {
704 + string_freez(p->name);
705 + p->name = GetProcessFriendlyNameSanitized(path);
706 + }
707
573 - DWORD unicode_size = sizeof(unicode) / sizeof(*unicode);
574 - if(QueryFullProcessImageNameW(hProcess, 0, unicode, &unicode_size)) {
575 - full_path = wchar_to_string(unicode);
576 - friendly_name = GetProcessFriendlyName(unicode);
708 + free(cmdline); // free(), not freez()
709 + }
710 }
711
579 - CloseHandle(hProcess);
712 + if(!p->cmdline || !p->name) {
713 + if (QueryFullProcessImageNameW(hProcess, 0, unicode, &unicode_size)) {
714 + // put the full path name to the command into cmdline
715 + if(!p->cmdline)
716 + p->cmdline = wchar_to_string(unicode);
717
581 - if(full_path) {
582 - string_freez(p->cmdline);
583 - p->cmdline = full_path;
718 + if(!p->name)
719 + p->name = GetProcessFriendlyNameSanitized(unicode);
720 + }
721 }
722
586 - if(friendly_name) {
723 + CloseHandle(hProcess);
724 +
725 + char *comm = wchar_to_utf8(pe32.szExeFile);
726 + fix_windows_comm(p, comm);
727 + update_pid_comm(p, comm); // will sanitize p->comm
728 +
729 + if(!need_service_names && string_strcmp(p->comm, "svchost") == 0)
730 + need_service_names = true;
731 +
732 + STRING *better_name = GetNameFromCmdlineSanitized(p);
733 + if(better_name) {
734 string_freez(p->name);
588 - p->name = friendly_name;
589 - p->assigned_to_target = false;
735 + p->name = better_name;
736 }
737 +
738 } while (Process32NextW(hSnapshot, &pe32));
739
740 CloseHandle(hSnapshot);
741 +
742 + if(need_service_names)
743 + GetServiceNames();
744 }
745
746 static inline kernel_uint_t perflib_cpu_utilization(COUNTER_DATA *d) {
@@ -692,40 +842,17 @@ bool apps_os_collect_all_pids_windows(void) {
842 // a new pid
843 p->initialized = true;
844
695 - static __thread char name[MAX_PATH];
696 -
697 - if (getInstanceName(d.pDataBlock, d.pObjectType, d.pi, name, sizeof(name))) {
698 - // remove the PID suffix, if any
699 - char pid[UINT64_MAX_LENGTH + 1]; // +1 for the underscore
700 - pid[0] = '_';
701 - print_uint64(&pid[1], p->pid);
702 - size_t pid_len = strlen(pid);
703 - size_t name_len = strlen(name);
704 - if (pid_len < name_len) {
705 - char *compare = &name[name_len - pid_len];
706 - if (strcmp(pid, compare) == 0)
707 - *compare = '\0';
708 - }
845 + static __thread char comm[MAX_PATH];
846
710 - // remove the .exe suffix, if any
711 - name_len = strlen(name);
712 - size_t exe_len = strlen(".exe");
713 - if(exe_len < name_len) {
714 - char *compare = &name[name_len - exe_len];
715 - if (strcmp(".exe", compare) == 0)
716 - *compare = '\0';
717 - }
718 - }
847 + if (getInstanceName(d.pDataBlock, d.pObjectType, d.pi, comm, sizeof(comm)))
848 + fix_windows_comm(p, comm);
849 else
720 - strncpyz(name, "unknown", sizeof(name) - 1);
850 + strncpyz(comm, "unknown", sizeof(comm) - 1);
851
722 - if(strcmp(name, "wininit") == 0)
852 + if(strcmp(comm, "wininit") == 0)
853 INIT_PID = p->pid;
854
725 - string_freez(p->comm); // it may be detected in a previous run via GetAllProcessesInfo()
726 - p->comm = string_strdupz(name);
727 - p->got_info = false;
728 - p->assigned_to_target = false;
855 + update_pid_comm(p, comm); // will sanitize p->comm
856 added++;
857
858 COUNTER_DATA ppid = {.key = "Creating Process ID"};
src/collectors/apps.plugin/apps_os_windows_nt.c new
+44
@@ -0,0 +1,44 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +// this must not include libnetdata.h because STRING is defined in winternl.h
4 +
5 +#include "config.h"
6 +#if defined(OS_WINDOWS)
7 +
8 +#include <windows.h>
9 +#include <winternl.h>
10 +#include <psapi.h>
11 +#include <stdint.h>
12 +
13 +// --------------------------------------------------------------------------------------------------------------------
14 +// Get the full windows command line
15 +
16 +WCHAR* GetProcessCommandLine(HANDLE hProcess) {
17 + PROCESS_BASIC_INFORMATION pbi;
18 + ULONG len;
19 + NTSTATUS status = NtQueryInformationProcess(hProcess, 0, &pbi, sizeof(pbi), &len);
20 + if (status != 0)
21 + return NULL;
22 +
23 + // The rest of the function remains the same as before
24 + PEB peb;
25 + if (!ReadProcessMemory(hProcess, pbi.PebBaseAddress, &peb, sizeof(peb), NULL))
26 + return NULL;
27 +
28 + RTL_USER_PROCESS_PARAMETERS procParams;
29 + if (!ReadProcessMemory(hProcess, peb.ProcessParameters, &procParams, sizeof(procParams), NULL))
30 + return NULL;
31 +
32 + WCHAR* commandLine = (WCHAR*)malloc(procParams.CommandLine.MaximumLength);
33 + if (!commandLine)
34 + return NULL;
35 +
36 + if (!ReadProcessMemory(hProcess, procParams.CommandLine.Buffer, commandLine, procParams.CommandLine.MaximumLength, NULL)) {
37 + free(commandLine);
38 + return NULL;
39 + }
40 +
41 + return commandLine;
42 +}
43 +
44 +#endif
src/collectors/apps.plugin/apps_pid.c
+52 -13
@@ -134,6 +134,7 @@ void del_pid_entry(pid_t pid) {
134 freez(p->fds);
135 #endif
136
137 + string_freez(p->comm_orig);
138 string_freez(p->comm);
139 string_freez(p->cmdline);
140 aral_freez(pids.all_pids.aral, p);
@@ -316,7 +317,49 @@ static inline void link_all_processes_to_their_parents(void) {
317
318 // --------------------------------------------------------------------------------------------------------------------
319
320 +static inline STRING *comm_from_cmdline_sanitized(char *comm, STRING *cmdline) {
321 + if(!cmdline) {
322 + sanitize_chart_meta(comm);
323 + return string_strdupz(comm);
324 + }
325 +
326 + const char *cl = string2str(cmdline);
327 + size_t len = string_strlen(cmdline);
328 +
329 + char buf_cmd[len + 1];
330 + // if it is enclosed in (), remove the parenthesis
331 + if(cl[0] == '(' && cl[len - 1] == ')') {
332 + memcpy(buf_cmd, &cl[1], len - 2);
333 + buf_cmd[len - 2] = '\0';
334 + }
335 + else
336 + memcpy(buf_cmd, cl, sizeof(buf_cmd));
337 +
338 + size_t comm_len = strlen(comm);
339 + char *start = strstr(buf_cmd, comm);
340 + if(start) {
341 + char *end = start + comm_len;
342 + while(*end && !isspace((uint8_t)*end) && *end != '/' && *end != '\\' && *end != '"') end++;
343 + *end = '\0';
344 +
345 + sanitize_chart_meta(start);
346 + return string_strdupz(start);
347 + }
348 +
349 + sanitize_chart_meta(comm);
350 + return string_strdupz(comm);
351 +}
352 +
353 void update_pid_comm(struct pid_stat *p, const char *comm) {
354 + if(p->comm_orig && string_strcmp(p->comm_orig, comm) == 0)
355 + // no change
356 + return;
357 +
358 +#if (PROCESSES_HAVE_CMDLINE == 1)
359 + if(likely(proc_pid_cmdline_is_needed && !p->cmdline))
360 + managed_log(p, PID_LOG_CMDLINE, read_proc_pid_cmdline(p));
361 +#endif
362 +
363 // some process names have ( and ), remove the parenthesis
364 size_t len = strlen(comm);
365 char buf[len + 1];
@@ -327,22 +370,18 @@ void update_pid_comm(struct pid_stat *p, const char *comm) {
370 else
371 memcpy(buf, comm, sizeof(buf));
372
330 - // check if the comm is changed
331 - if(!p->comm || strcmp(pid_stat_comm(p), buf) != 0) {
332 - // it is changed
373 + string_freez(p->comm_orig);
374 + p->comm_orig = string_strdupz(comm);
375
334 - string_freez(p->comm);
335 - p->comm = string_strdupz(buf);
376 + string_freez(p->comm);
377 + p->comm = comm_from_cmdline_sanitized(buf, p->cmdline);
378
337 -#if (PROCESSES_HAVE_CMDLINE == 1)
338 - if(likely(proc_pid_cmdline_is_needed))
339 - managed_log(p, PID_LOG_CMDLINE, read_proc_pid_cmdline(p));
340 -#endif
379 + p->is_manager = is_process_manager(p);
380 + p->is_aggregator = is_process_aggregator(p);
381
342 - // the process changes comm, we may have to reassign it to
343 - // an apps_groups.conf target.
344 - p->target = NULL;
345 - }
382 + // the process changed comm, we may have to reassign it to
383 + // an apps_groups.conf target.
384 + p->target = NULL;
385 }
386
387 // --------------------------------------------------------------------------------------------------------------------
src/collectors/apps.plugin/apps_plugin.c
+90 -1
@@ -121,6 +121,84 @@ size_t pagesize;
121 // ----------------------------------------------------------------------------
122 // update chart dimensions
123
124 +// Helper function to count the number of processes in the linked list
125 +int count_processes(struct pid_stat *root) {
126 + int count = 0;
127 +
128 + for(struct pid_stat *p = root; p ; p = p->next)
129 + if(p->updated) count++;
130 +
131 + return count;
132 +}
133 +
134 +// Comparator function to sort by pid
135 +int compare_by_pid(const void *a, const void *b) {
136 + struct pid_stat *pa = *(struct pid_stat **)a;
137 + struct pid_stat *pb = *(struct pid_stat **)b;
138 + return ((int)pa->pid - (int)pb->pid);
139 +}
140 +
141 +// Function to print a process and its children recursively
142 +void print_process_tree(struct pid_stat *root, struct pid_stat *parent, int depth, int total_processes) {
143 + // Allocate an array of pointers for processes with the given parent
144 + struct pid_stat **children = (struct pid_stat **)malloc(total_processes * sizeof(struct pid_stat *));
145 + int children_count = 0;
146 +
147 + // Populate the array with processes that have the given parent
148 + struct pid_stat *p = root;
149 + while (p != NULL) {
150 + if (p->updated && p->parent == parent) {
151 + children[children_count++] = p;
152 + }
153 + p = p->next;
154 + }
155 +
156 + // Sort the children array by pid
157 + qsort(children, children_count, sizeof(struct pid_stat *), compare_by_pid);
158 +
159 + // Print each child and recurse
160 + for (int i = 0; i < children_count; i++) {
161 + // Print the current process with indentation based on depth
162 + if (depth > 0) {
163 + for (int j = 0; j < (depth - 1) * 4; j++) {
164 + printf(" ");
165 + }
166 + printf(" \\_ ");
167 + }
168 +
169 +#if (PROCESSES_HAVE_COMM_AND_NAME == 1)
170 + printf("[%d] %s (name: %s) [%s]: %s\n", children[i]->pid,
171 + string2str(children[i]->comm),
172 + string2str(children[i]->name),
173 + string2str(children[i]->target->name),
174 + string2str(children[i]->cmdline));
175 +#else
176 + printf("[%d] %s [%s]: %s\n", children[i]->pid,
177 + string2str(children[i]->comm),
178 + string2str(children[i]->target->name),
179 + string2str(children[i]->cmdline));
180 +#endif
181 +
182 + // Recurse to print this child's children
183 + print_process_tree(root, children[i], depth + 1, total_processes);
184 + }
185 +
186 + // Free the allocated array
187 + free(children);
188 +}
189 +
190 +// Function to print the full hierarchy
191 +void print_hierarchy(struct pid_stat *root) {
192 + // Count the total number of processes
193 + int total_processes = count_processes(root);
194 +
195 + // Start printing from processes with parent = NULL (i.e., root processes)
196 + print_process_tree(root, NULL, 0, total_processes);
197 +}
198 +
199 +// ----------------------------------------------------------------------------
200 +// update chart dimensions
201 +
202 #if (ALL_PIDS_ARE_READ_INSTANTLY == 0)
203 static void normalize_utilization(struct target *root) {
204 struct target *w;
@@ -297,6 +375,7 @@ cleanup:
375 }
376
377 static bool profile_speed = false;
378 +static bool print_tree_and_exit = false;
379
380 static void parse_args(int argc, char **argv)
381 {
@@ -316,6 +395,11 @@ static void parse_args(int argc, char **argv)
395 exit(0);
396 }
397
398 + if(strcmp("print", argv[i]) == 0 || strcmp("-print", argv[i]) == 0 || strcmp("--print", argv[i]) == 0) {
399 + print_tree_and_exit = true;
400 + continue;
401 + }
402 +
403 #if defined(OS_LINUX)
404 if(strcmp("test-permissions", argv[i]) == 0 || strcmp("-t", argv[i]) == 0) {
405 if(!check_proc_1_io()) {
@@ -618,7 +702,7 @@ int main(int argc, char **argv) {
702 procfile_adaptive_initial_allocation = 1;
703 os_get_system_HZ();
704 os_get_system_cpus_uncached();
621 - apps_orchestrators_and_aggregators_init(); // before parsing args!
705 + apps_managers_and_aggregators_init(); // before parsing args!
706 parse_args(argc, argv);
707
708 #if !defined(OS_WINDOWS)
@@ -702,6 +786,11 @@ int main(int argc, char **argv) {
786 normalize_utilization(apps_groups_root_target);
787 #endif
788
789 + if(unlikely(print_tree_and_exit)) {
790 + print_hierarchy(root_of_pids());
791 + exit(0);
792 + }
793 +
794 if(send_resource_usage)
795 send_resource_usage_to_netdata(dt);
796
src/collectors/apps.plugin/apps_plugin.h
+17 -12
@@ -371,7 +371,10 @@ struct target {
371
372 TARGET_TYPE type;
373 union {
374 - STRING *compare;
374 + struct {
375 + SIMPLE_PATTERN *pattern;
376 + STRING *compare;
377 + } ag;
378 #if (PROCESSES_HAVE_UID == 1)
379 uid_t uid;
380 #endif
@@ -393,11 +396,8 @@ struct target {
396 #endif
397
398 bool exposed:1; // if set, we have sent this to netdata
396 - bool hidden:1; // if set, we set the hidden flag on the dimension
397 - bool debug_enabled:1;
398 - bool ends_with:1;
399 - bool starts_with:1; // if set, the compare string matches only the
400 - // beginning of the command
399 + bool ends_with:1; // if set, the compare string matches the end of the command
400 + bool starts_with:1; // if set, the compare string matches the start of the command
401
402 struct pid_on_target *root_pid; // list of aggregated pids for target debugging
403
@@ -476,7 +476,7 @@ struct pid_stat {
476 struct pid_stat *next;
477 struct pid_stat *prev;
478
479 - struct target *target; // app_groups.conf targets
479 + struct target *target; // app_groups.conf/tree targets
480
481 #if (PROCESSES_HAVE_UID == 1)
482 struct target *uid_target; // uid based targets
@@ -485,9 +485,10 @@ struct pid_stat {
485 struct target *gid_target; // gid based targets
486 #endif
487
488 - STRING *comm; // the command name (short version)
489 - STRING *name; // a better name, or NULL
490 - STRING *cmdline; // the full command line (or on windows, the full pathname of the program)
488 + STRING *comm_orig; // the command, as-collected
489 + STRING *comm; // the command, sanitized
490 + STRING *name; // the command name if any, sanitized
491 + STRING *cmdline; // the full command line of the program
492
493 #if defined(OS_WINDOWS)
494 COUNTER_DATA perflib[PDF_MAX];
@@ -531,6 +532,8 @@ struct pid_stat {
532 bool updated:1; // true when the process is currently running
533 bool merged:1; // true when it has been merged to its parent
534 bool keep:1; // true when we need to keep this process in memory even after it exited
535 + bool is_manager:1; // true when this pid is a process manager
536 + bool is_aggregator:1; // true when this pid is a process aggregator
537
538 bool matched_by_config:1;
539
@@ -540,7 +543,7 @@ struct pid_stat {
543
544 #if defined(OS_WINDOWS)
545 bool got_info:1;
543 - bool assigned_to_target:1;
546 + bool got_service:1;
547 bool initialized:1;
548 #endif
549
@@ -631,7 +634,7 @@ bool managed_log(struct pid_stat *p, PID_LOG log, bool status);
634 #define pid_incremental_cpu(type, idx, value) \
635 incremental_rate(p->values[idx], p->raw[idx], value, p->type##_collected_usec, p->last_##type##_collected_usec, CPU_TO_NANOSECONDCORES)
636
634 -void apps_orchestrators_and_aggregators_init(void);
637 +void apps_managers_and_aggregators_init(void);
638 void apps_users_and_groups_init(void);
639 void apps_pids_init(void);
640
@@ -675,6 +678,8 @@ struct pid_stat *find_pid_entry(pid_t pid);
678 void del_pid_entry(pid_t pid);
679 void update_pid_comm(struct pid_stat *p, const char *comm);
680
681 +bool is_process_manager(struct pid_stat *p);
682 +bool is_process_aggregator(struct pid_stat *p);
683
684 // --------------------------------------------------------------------------------------------------------------------
685 // targets management
src/collectors/apps.plugin/apps_targets.c
+56 -94
@@ -31,35 +31,7 @@ struct target *find_target_by_name(struct target *base, const char *name) {
31 }
32
33 // --------------------------------------------------------------------------------------------------------------------
34 -// Tree
35 -
36 -static inline STRING *comm_from_cmdline(STRING *comm, STRING *cmdline) {
37 - if(!cmdline) return sanitize_chart_meta_string(comm);
38 -
39 - const char *cl = string2str(cmdline);
40 - size_t len = string_strlen(cmdline);
41 -
42 - char buf_cmd[len + 1];
43 - // if it is enclosed in (), remove the parenthesis
44 - if(cl[0] == '(' && cl[len - 1] == ')') {
45 - memcpy(buf_cmd, &cl[1], len - 2);
46 - buf_cmd[len - 2] = '\0';
47 - }
48 - else
49 - memcpy(buf_cmd, cl, sizeof(buf_cmd));
50 -
51 - char *start = strstr(buf_cmd, string2str(comm));
52 - if(start) {
53 - char *end = start + string_strlen(comm);
54 - while(*end && !isspace((uint8_t)*end) && *end != '/' && *end != '\\') end++;
55 - *end = '\0';
56 -
57 - sanitize_chart_meta(start);
58 - return string_strdupz(start);
59 - }
60 -
61 - return sanitize_chart_meta_string(comm);
62 -}
34 +// Process managers and aggregators
35
36 struct comm_list {
37 STRING *comm;
@@ -111,21 +83,26 @@ static void managed_list_add(struct managed_list *list, const char *s) {
83
84 static STRING *KernelAggregator = NULL;
85
114 -void apps_orchestrators_and_aggregators_init(void) {
86 +void apps_managers_and_aggregators_init(void) {
87 KernelAggregator = string_strdupz("kernel");
88
89 managed_list_clear(&tree.managers);
90 #if defined(OS_LINUX)
119 - managed_list_add(&tree.managers, "init"); // linux systems
120 - managed_list_add(&tree.managers, "systemd"); // lxc containers and host systems (this also catches "systemd --user")
121 - managed_list_add(&tree.managers, "containerd-shim"); // docker containers
122 - managed_list_add(&tree.managers, "docker-init"); // docker containers
123 - managed_list_add(&tree.managers, "dumb-init"); // some docker containers use this
124 - managed_list_add(&tree.managers, "gnome-shell"); // gnome user applications
91 + managed_list_add(&tree.managers, "init"); // linux systems
92 + managed_list_add(&tree.managers, "systemd"); // lxc containers and host systems (this also catches "systemd --user")
93 + managed_list_add(&tree.managers, "containerd-shim-runc-v2"); // docker containers
94 + managed_list_add(&tree.managers, "docker-init"); // docker containers
95 + managed_list_add(&tree.managers, "dumb-init"); // some docker containers use this
96 + managed_list_add(&tree.managers, "openrc-run.sh"); // openrc
97 + managed_list_add(&tree.managers, "crond"); // linux crond
98 + managed_list_add(&tree.managers, "gnome-shell"); // gnome user applications
99 + managed_list_add(&tree.managers, "plasmashell"); // kde user applications
100 + managed_list_add(&tree.managers, "xfwm4"); // xfce4 user applications
101 #elif defined(OS_WINDOWS)
126 - managed_list_add(&tree.managers, "System");
127 - managed_list_add(&tree.managers, "services");
102 managed_list_add(&tree.managers, "wininit");
103 + managed_list_add(&tree.managers, "services");
104 + managed_list_add(&tree.managers, "explorer");
105 + managed_list_add(&tree.managers, "System");
106 #elif defined(OS_FREEBSD)
107 managed_list_add(&tree.managers, "init");
108 #elif defined(OS_MACOS)
@@ -142,49 +119,52 @@ void apps_orchestrators_and_aggregators_init(void) {
119 #endif
120 }
121
145 -static inline bool is_orchestrator(struct pid_stat *p) {
122 +bool is_process_manager(struct pid_stat *p) {
123 for(size_t c = 0; c < tree.managers.used ; c++) {
147 - if(p->comm == tree.managers.array[c].comm)
124 + if(p->comm == tree.managers.array[c].comm ||
125 + p->comm_orig == tree.managers.array[c].comm)
126 return true;
127 }
128
129 return false;
130 }
131
154 -static inline bool is_aggregator(struct pid_stat *p) {
132 +bool is_process_aggregator(struct pid_stat *p) {
133 for(size_t c = 0; c < tree.aggregators.used ; c++) {
156 - if(p->comm == tree.aggregators.array[c].comm)
134 + if(p->comm == tree.aggregators.array[c].comm ||
135 + p->comm_orig == tree.aggregators.array[c].comm)
136 return true;
137 }
138
139 return false;
140 }
141
142 +// --------------------------------------------------------------------------------------------------------------------
143 +// Tree
144 +
145 struct target *get_tree_target(struct pid_stat *p) {
146 // // skip fast all the children that are more than 3 levels down
147 // while(p->parent && p->parent->pid != INIT_PID && p->parent->parent && p->parent->parent->parent)
148 // p = p->parent;
149
150 // keep the children of INIT_PID, and process orchestrators
169 - while(p->parent && p->parent->pid != INIT_PID && p->parent->pid != 0 && !is_orchestrator(p->parent))
151 + while(p->parent && p->parent->pid != INIT_PID && p->parent->pid != 0 && !p->parent->is_manager)
152 p = p->parent;
153
154 // merge all processes into process aggregators
173 - STRING *search_for = string_dup(p->comm);
174 - bool aggregator = false;
175 - if((p->ppid == 0 && p->pid != INIT_PID) || (p->parent && is_aggregator(p->parent))) {
176 - aggregator = true;
155 + STRING *search_for = NULL;
156 + if((p->ppid == 0 && p->pid != INIT_PID) || (p->parent && p->parent->is_aggregator)) {
157 search_for = string_dup(KernelAggregator);
158 }
179 -
180 - if(!aggregator) {
159 + else {
160 #if (PROCESSES_HAVE_COMM_AND_NAME == 1)
182 - search_for = sanitize_chart_meta_string(p->name ? p->name : p->comm);
161 + search_for = string_dup(p->name ? p->name : p->comm);
162 #else
184 - search_for = comm_from_cmdline(p->comm, p->cmdline);
163 + search_for = string_dup(p->comm);
164 #endif
165 }
166
167 + // find an existing target with the required name
168 struct target *w;
169 for(w = apps_groups_root_target; w ; w = w->next) {
170 if (w->name == search_for) {
@@ -196,7 +176,7 @@ struct target *get_tree_target(struct pid_stat *p) {
176 w = callocz(sizeof(struct target), 1);
177 w->type = TARGET_TYPE_TREE;
178 w->starts_with = w->ends_with = false;
199 - w->compare = string_dup(p->comm);
179 + w->ag.compare = string_dup(search_for);
180 w->id = search_for;
181 w->name = string_dup(search_for);
182 w->clean_name = get_clean_name(w->name);
@@ -302,17 +282,17 @@ struct target *apps_groups_root_target = NULL;
282
283 // find or create a new target
284 // there are targets that are just aggregated to other target (the second argument)
305 -static struct target *get_apps_groups_target(const char *id, struct target *target, const char *name) {
306 - bool tdebug = false, thidden = target ? target->hidden : false, ends_with = false, starts_with = false;
285 +static struct target *get_apps_groups_target(const char *comm, struct target *target, const char *name) {
286 + bool ends_with = false, starts_with = false, has_asterisk_inside = false;
287
308 - STRING *id_lookup = NULL;
288 + STRING *comm_lookup = NULL;
289 STRING *name_lookup = NULL;
290
291 // extract the options from the id
292 {
313 - size_t len = strlen(id);
293 + size_t len = strlen(comm);
294 char buf[len + 1];
315 - memcpy(buf, id, sizeof(buf));
295 + memcpy(buf, comm, sizeof(buf));
296
297 if(buf[len - 1] == '*') {
298 buf[--len] = '\0';
@@ -320,37 +300,25 @@ static struct target *get_apps_groups_target(const char *id, struct target *targ
300 }
301
302 const char *nid = buf;
323 - while (nid[0] == '-' || nid[0] == '+' || nid[0] == '*') {
324 - if (nid[0] == '-') thidden = true;
325 - if (nid[0] == '+') tdebug = true;
326 - if (nid[0] == '*') ends_with = true;
303 + if (nid[0] == '*') {
304 + ends_with = true;
305 nid++;
306 }
307
330 - id_lookup = string_strdupz(nid);
308 + if(strchr(nid, '*'))
309 + has_asterisk_inside = true;
310 +
311 + comm_lookup = string_strdupz(nid);
312 }
313
314 // extract the options from the name
334 - {
335 - size_t len = strlen(name);
336 - char buf[len + 1];
337 - memcpy(buf, name, sizeof(buf));
338 -
339 - const char *nn = buf;
340 - while (nn[0] == '-' || nn[0] == '+') {
341 - if (nn[0] == '-') thidden = true;
342 - if (nn[0] == '+') tdebug = true;
343 - nn++;
344 - }
345 -
346 - name_lookup = string_strdupz(nn);
347 - }
315 + name_lookup = string_strdupz(name);
316
317 // find if it already exists
318 struct target *w, *last = apps_groups_root_target;
319 for(w = apps_groups_root_target ; w ; w = w->next) {
352 - if(w->id == id_lookup) {
353 - string_freez(id_lookup);
320 + if(w->id == comm_lookup) {
321 + string_freez(comm_lookup);
322 string_freez(name_lookup);
323 return w;
324 }
@@ -368,19 +336,22 @@ static struct target *get_apps_groups_target(const char *id, struct target *targ
336
337 if(target && target->target)
338 fatal("Internal Error: request to link process '%s' to target '%s' which is linked to target '%s'",
371 - id, string2str(target->id), string2str(target->target->id));
339 + comm, string2str(target->id), string2str(target->target->id));
340
341 w = callocz(sizeof(struct target), 1);
342 w->type = TARGET_TYPE_APP_GROUP;
375 - w->compare = string_dup(id_lookup);
343 + w->ag.compare = string_dup(comm_lookup);
344 w->starts_with = starts_with;
345 w->ends_with = ends_with;
378 - w->id = string_dup(id_lookup);
346 + w->id = string_dup(comm_lookup);
347 +
348 + if(has_asterisk_inside)
349 + w->ag.pattern = simple_pattern_create(comm, " ", SIMPLE_PATTERN_EXACT, true);
350
351 if(unlikely(!target))
352 w->name = string_dup(name_lookup); // copy the name
353 else
383 - w->name = string_dup(id_lookup); // copy the id
354 + w->name = string_dup(comm_lookup); // copy the id
355
356 // dots are used to distinguish chart type and id in streaming, so we should replace them
357 w->clean_name = get_clean_name(w->name);
@@ -388,29 +359,20 @@ static struct target *get_apps_groups_target(const char *id, struct target *targ
359 if(w->starts_with && w->ends_with)
360 proc_pid_cmdline_is_needed = true;
361
391 - w->hidden = thidden;
392 -#ifdef NETDATA_INTERNAL_CHECKS
393 - w->debug_enabled = tdebug;
394 -#else
395 - if(tdebug)
396 - fprintf(stderr, "apps.plugin has been compiled without debugging\n");
397 -#endif
362 w->target = target;
363
364 // append it, to maintain the order in apps_groups.conf
365 if(last) last->next = w;
366 else apps_groups_root_target = w;
367
404 - debug_log("ADDING TARGET ID '%s', process name '%s' (%s), aggregated on target '%s', options: %s %s"
368 + debug_log("ADDING TARGET ID '%s', process name '%s' (%s), aggregated on target '%s'"
369 , string2str(w->id)
406 - , string2str(w->compare)
370 + , string2str(w->ag.compare)
371 , (w->starts_with && w->ends_with)?"substring":((w->starts_with)?"prefix":((w->ends_with)?"suffix":"exact"))
372 , w->target?w->target->name:w->name
409 - , (w->hidden)?"hidden":"-"
410 - , (w->debug_enabled)?"debug":"-"
373 );
374
413 - string_freez(id_lookup);
375 + string_freez(comm_lookup);
376 string_freez(name_lookup);
377
378 return w;
src/collectors/windows-events.plugin/windows-events.c
+9
@@ -1139,6 +1139,15 @@ int main(int argc __maybe_unused, char **argv __maybe_unused) {
1139 sid_cache_init();
1140 field_cache_init();
1141
1142 + if(!EnableWindowsPrivilege(SE_SECURITY_NAME))
1143 + nd_log(NDLS_COLLECTORS, NDLP_WARNING, "Failed to enable %s privilege", SE_SECURITY_NAME);
1144 +
1145 + if(!EnableWindowsPrivilege(SE_BACKUP_NAME))
1146 + nd_log(NDLS_COLLECTORS, NDLP_WARNING, "Failed to enable %s privilege", SE_BACKUP_NAME);
1147 +
1148 + if(!EnableWindowsPrivilege(SE_AUDIT_NAME))
1149 + nd_log(NDLS_COLLECTORS, NDLP_WARNING, "Failed to enable %s privilege", SE_AUDIT_NAME);
1150 +
1151 // ------------------------------------------------------------------------
1152 // debug
1153
src/health/health_notifications.c
+25 -10
@@ -20,17 +20,27 @@ struct health_raised_summary {
20 };
21
22 void health_alarm_wait_for_execution(ALARM_ENTRY *ae) {
23 - if (!(ae->flags & HEALTH_ENTRY_FLAG_EXEC_IN_PROGRESS))
24 - return;
23 + // this has to ALWAYS remove the given alarm entry from the queue
24
26 - if(!ae->popen_instance) {
27 - // nd_log(NDLS_DAEMON, NDLP_ERR, "attempted to wait for the execution of alert that has not spawn a notification");
28 - return;
25 + int code = 0;
26 +
27 + if (!(ae->flags & HEALTH_ENTRY_FLAG_EXEC_IN_PROGRESS)) {
28 + nd_log(NDLS_DAEMON, NDLP_ERR, "attempted to wait for the execution of alert that has not an execution in progress");
29 + code = 128;
30 + goto cleanup;
31 }
32
31 - ae->exec_code = spawn_popen_wait(ae->popen_instance);
33 + if(!ae->popen_instance) {
34 + nd_log(NDLS_DAEMON, NDLP_ERR, "attempted to wait for the execution of alert that has not spawn a notification");
35 + code = 128;
36 + goto cleanup;
37 + }
38
39 + code = spawn_popen_wait(ae->popen_instance);
40 netdata_log_debug(D_HEALTH, "done executing command - returned with code %d", ae->exec_code);
41 +
42 +cleanup:
43 + ae->exec_code = code;
44 ae->flags &= ~HEALTH_ENTRY_FLAG_EXEC_IN_PROGRESS;
45
46 if(ae->exec_code != 0)
@@ -466,13 +476,18 @@ void health_send_notification(RRDHOST *host, ALARM_ENTRY *ae, struct health_rais
476 ae->exec_run_timestamp = now_realtime_sec(); /* will be updated by real time after spawning */
477
478 netdata_log_debug(D_HEALTH, "executing command '%s'", command_to_run);
469 - ae->flags |= HEALTH_ENTRY_FLAG_EXEC_IN_PROGRESS;
479 ae->popen_instance = spawn_popen_run(command_to_run);
471 - enqueue_alarm_notify_in_progress(ae);
480 + if(ae->popen_instance) {
481 + ae->flags |= HEALTH_ENTRY_FLAG_EXEC_IN_PROGRESS;
482 + enqueue_alarm_notify_in_progress(ae);
483 + }
484 + else
485 + netdata_log_error("Failed to execute alarm notification");
486 +
487 health_alarm_log_save(host, ae);
473 - } else {
474 - netdata_log_error("Failed to format command arguments");
488 }
489 + else
490 + netdata_log_error("Failed to format command arguments");
491
492 buffer_free(warn_alarms);
493 buffer_free(crit_alarms);
src/libnetdata/os/os-windows-wrappers.c
+38
@@ -58,4 +58,42 @@ bool netdata_registry_get_string(char *out, unsigned int length, void *hKey, cha
58 return status;
59 }
60
61 +bool EnableWindowsPrivilege(const char *privilegeName) {
62 + HANDLE hToken;
63 + LUID luid;
64 + TOKEN_PRIVILEGES tkp;
65 +
66 + // Open the process token with appropriate access rights
67 + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
68 + return false;
69 +
70 + // Lookup the LUID for the specified privilege
71 + if (!LookupPrivilegeValue(NULL, privilegeName, &luid)) {
72 + CloseHandle(hToken); // Close the token handle before returning
73 + return false;
74 + }
75 +
76 + // Set up the TOKEN_PRIVILEGES structure
77 + tkp.PrivilegeCount = 1;
78 + tkp.Privileges[0].Luid = luid;
79 + tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
80 +
81 + // Adjust the token's privileges
82 + if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL)) {
83 + CloseHandle(hToken); // Close the token handle before returning
84 + return false;
85 + }
86 +
87 + // Check if AdjustTokenPrivileges succeeded
88 + if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) {
89 + CloseHandle(hToken); // Close the token handle before returning
90 + return false;
91 + }
92 +
93 + // Close the handle to the token after success
94 + CloseHandle(hToken);
95 +
96 + return true;
97 +}
98 +
99 #endif
src/libnetdata/os/os-windows-wrappers.h
+2
@@ -14,5 +14,7 @@ bool netdata_registry_get_dword(unsigned int *out, void *hKey, char *subKey, cha
14 long netdata_registry_get_string_from_open_key(char *out, unsigned int length, void *lKey, char *name);
15 bool netdata_registry_get_string(char *out, unsigned int length, void *hKey, char *subKey, char *name);
16
17 +bool EnableWindowsPrivilege(const char *privilegeName);
18 +
19 #endif // OS_WINDOWS
20 #endif //NETDATA_OS_WINDOWS_WRAPPERS_H
src/libnetdata/spawn_server/spawn_server_windows.c
+3 -1
@@ -54,7 +54,7 @@ static BUFFER *argv_to_windows(const char **argv) {
54 BUFFER *wb = buffer_create(0, NULL);
55
56 // argv[0] is the path
57 - char b[strlen(argv[0]) * 2 + 1024];
57 + char b[strlen(argv[0]) * 2 + FILENAME_MAX];
58 cygwin_conv_path(CCP_POSIX_TO_WIN_A | CCP_ABSOLUTE, argv[0], b, sizeof(b));
59
60 for(size_t i = 0; argv[i] ;i++) {
@@ -84,6 +84,8 @@ static BUFFER *argv_to_windows(const char **argv) {
84 else
85 buffer_putc(wb, ' ');
86 }
87 + else if (needs_quotes)
88 + buffer_putc(wb, '"');
89
90 for(const char *c = s; *c ; c++) {
91 switch(*c) {