One cannot.
Everything that systemd provides to a process invoked for a service — its arguments, its environment, the control group(s) containing it, its resource limits, its open file descriptors — can be and is done by other service managers, too. Moreover, there is no shared convention for identifying what service manager is managing services; no conventional environment variables, nor other markers.
Erroneous mechanisms
You might be thinking of checking the executable name of the parent process. This is a non-starter, for the reasons expounded in https://unix.stackexchange.com/a/196252/5132 . The name of the executable program image of the parent process (for system-wide services) will be /sbin/init
on Debian Linux operating systems, because Debian has the convention of that being an alternatives-style symbolic link to the actual process 1 program image file, and the the /init
program in the initramfs only needing to know that one name.
You might be thinking that, despite what I just wrote, control groups are a systemd marker. They are not. Here's the control group tree of a service process being managed by a completely different service manager, service-manager
from the nosh toolset:
% systemd-cgls /system.slice/service-manager.service/tinydns@.service
Control group /system.slice/service-manager.service/tinydns@.service:
└─tinydns@127.53.0.1.service
└─1433 tinydns
%
The tinydns
program, finding a control group in /proc/self/cgroup
, has no justification for assuming that systemd set up that control group. It was actually set up by the move-to-control-group
utility:
% cat /var/local/sv/tinydns@127.53.0.1/service/run
#!/bin/nosh
#Run file generated from ./tinydns@.socket
#DNS/UDP socket on 127.53.0.1
udp-socket-listen --systemd-compatibility --combine4and6 127.53.0.1 domain
move-to-control-group "../tinydns@.service/tinydns@127.53.0.1.service"
envdir env
envuidgid -- tinydns-d
setlogin -- tinydns-d
hardlimit -d 3000000
softlimit -d hard
./service
%
No other process state changes are unique to systemd. Environment variables can be set with setenv
(the chain-loading tool) userenv
, machineenv
, or export
(the chain-loading tool), resource limits with softlimit
, ulimit
(the chain-loading tool), or s6-softlimit
, open file descriptors with redirfd
or fdredir
, namespaces with unshare
, scheduling priorities with rtprio
or chrt
, NUMA policy with numactl
; and so forth.
systemd is not the only speaker of the LISTEN_FDS
protocol, as can be seen from the aforegiven. INVOCATION_ID
is likewise just an exercise in populating env/INVOCATION_ID
at the start of a run
program and chaining through envdir
. None of these are reliable as markers.
Conceptual errors
There's also the flaw that you want to exclude processes started by cron
. The conceptual flaw here is that cron
is a service, and the processes that it spawns are running in the context of that service. There is no magic distinction between a process spawned by the cron
process in the cron
service and a process spawned by some other service process in some other service, that makes the former somehow distinguishable from the latter.
Rid yourself of this conceptual error, and an answer appears. The thing that distinguishes dæmons is that the (POSIX) sessions that they belong to do not have controlling terminals, and they have no other associations (from the name set by setlogin
, through systemd's user-space login session mechanism, to various security contexts) with any login session. There's not a portable direct way to query what the controlling terminal of a session is, but noting that opening /dev/tty
fails is an available indirect route. Note that some superficially promising C library functions are actually unreliable in practice.
Further reading