RUNLEVEL not defined

env var RUNLEVEL seems not to be defined with systemd.

Systemctl status show “loaded” in (runlevel 5) and in (runlevel 3)
Same thing with

In that link :
I see that is loaded either in runlevel 2 and runlevel 3.

Any way to get a runlevel easily ?

Any help is welcome.

systemd does not have runlevels. You should explain what you need (are trying) to do, not how you believe it should be done.

The quick answer is – no.

The long answer is:

  1. “man runlevel”
  2. This StackExchange discussion: <>

“Runlevels” are an obsolete way to start and stop groups of services used in SysV init. systemd provides a compatibility layer that maps runlevels to targets, and associated binaries like runlevel. Nevertheless, only one runlevel can be “active” at a given time, while systemd can activate multiple targets concurrently, so the mapping to runlevels is confusing and only approximate. Runlevels should not be used in new code, and are mostly useful as a shorthand way to refer the matching systemd targets in kernel boot parameters.

> of course I know that.

It is simple, I have a script which just need to know if the system booted in text mode ( old runlevel 3 ) or in graphical mode ( old runlevel 5 ).

In short I just need the old RUNLEVEL variable.

Use who or systemctl (as user)…

who -r
         run-level 5  2020-12-06 21:08

systemctl get-default

 > echo $RUNLEVEL

 > echo $PREVLEVEL

 > runlevel
Der absolute Pfad für 'runlevel' ist '/sbin/runlevel', daher kann das Programm möglicherweise nur von Benutzern mit Superuser-Rechten gestartet werden (z. B. root).

Sorry – “LANG=C” doesn’t help – you’ll have to live with the German … :wink:

 # echo $RUNLEVEL

 # echo $PREVLEVEL

 # runlevel
N 5
 # echo $RUNLEVEL

 # echo $PREVLEVEL


Hmmm – despite the man page, it doesn’t do what it indicates what it may do – maybe …

  • I’ll dig further …

Digging around revealed this:

 > who -r
         Runlevel 5   2020-12-08 10:45

Couldn’t get “cut” to work – “awk” functions perfectly …

 > who -r | awk '{print $2}'
 > RUNLEVEL=$(who -r | awk '{print $2}')
 > echo $RUNLEVEL

This is on Leap 15.2:

henk@boven:~> /sbin/runlevel
N 5


man runlevel

runlevel prints the previous and current SysV runlevel if they are known.

Which is not what the OP asked for. He asked

to know if the system booted in text mode ( old runlevel 3 ) or in graphical mode ( old runlevel 5 ).

But runlevel does not show that, but what it is. And of course things could have changed several times since the boot was done.

And of course (I quote the man page again):

“Runlevels” are an obsolete way to start and stop groups of services used in SysV init. systemd provides a compatibility layer that maps runlevels to targets, and associated binaries like runlevel.

I would say that using runlevels is not really future-proof. :frowning:

Agreed – they’re deprecated …

  • But, there’re ancient scripts out there in the wild which want to use the “RUNLEVEL” shell variable – which is deprecated …

Until such time as the scripts get rewritten → taking a spade to the graveyard and exhuming the corpse:

 > RUNLEVEL=$(who -r | awk '{print $2}')

who -r displays record in utmp. Under systemd this record is filled in by systemd service that runs on boot and basically is equivalent to “it is run-level 5 is is being activated else it is run-level 3 if is being activated else it is run-level 1 if is being activated”. As long as you ignore booting into custom unit (using systemd.unit option) where runlevel is not defined at all it is probably the most usable option under systemd currently.

systemctl get-default link may change after boot; it does not answer “what unit system has been booted into”. systemd does not preserve this information. It is actually trivial to add if someone can present valid use case when this information is necessary.

It does not even show “what unit system has been booted into” when link is not changed. It is only a default.
It is the target the system will boot into when nothing contrary is specified. You can e.g. use Grub to specify what target the system should boot into. That does not change the default. After that you can change the default, but that does not change the target in use at all. And when you change the target (systemctl isolate …) 10 times after boot? (May there is some logging to see which target the system was was booted in some months ago).

I don’t know if your description makes any sense.

A Linux system always boots to text mode ( before optionally continuing to load a DE (
And, Linux systems are multi-user (unlike Windows) so I’m guessing you should actually be querying whether the default display is actively used and maybe test for a function of that running DE.

I’m guessing that there is not even a purpose to this whole exercise.
Why should you even care if a person is logged in interactively or not? Do you intend to display something to the User, If so there are ways to send a notification to every active display on the machine which would render your wanting to know a person is logged in and running a graphical DE moot.

The way I look at this is that what you’re asking for doesn’t conform with how a Linux system works…


Thank you very much.
You’re the only one who answered the question.

Apart from malcolmlewis’s only response, is there any systemd guru to find a solution with systemd.
It is good to comment on the questions asked, but it is still better to answer them.

 > RUNLEVEL=$(who -r | awk '{print $2}')
 > echo $RUNLEVEL

In other words, “the good old RUNLEVEL variable” …

Please be clear on “type” – as in “integer” or “string” …
[HR][/HR]For the RUNLEVEL solution, one should declare the variable RUNLEVEL to be an integer and, possibly, after the assignment of the value, make it read-only:

 > declare -i RUNLEVEL
 > RUNLEVEL=$(who -r | awk '{print $2}')
 > echo $RUNLEVEL
 > declare -r RUNLEVEL
 > declare -p RUNLEVEL

[HR][/HR]For the case of “systemd-only”, unfortunately, systemd has at any point in time, several targets active in parallel …

systemctl list-units --type target --state active
  • N.B.: In Bash, a name has to be word consisting only of alphanumeric characters and underscores, and beginning with an alphabetic character or an underscore.

The variable used to store the current active target of interest has to be of type “string” – use “declare” to make sure of this …

 > declare Current_Target
 > Current_Target=$(systemctl list-units --type target --state active | grep -E 'rescue|emergency|graphical|multi-user' | head -1 | awk '{print $1}')
 > echo $Current_Target
 > declare -p Current_Target

Possibly, check that the system is in fact running –

 > systemctl is-system-running

Thank you very much.

Thanks again