systemd / pystemd

A thin Cython-based wrapper on top of libsystemd, focused on exposing the dbus API via sd-bus in an automated and easy to consume way.
GNU Lesser General Public License v2.1
411 stars 36 forks source link

Unit names with @ symbol throw an Exception #20

Closed dabcoder closed 5 years ago

dabcoder commented 5 years ago

Hello,

For units that have an @ symbol in their names, such as serial-getty@.service, pystemd throws an Exception. E.g:

File "pystemd/dbuslib.pyx", line 437, in pystemd.dbuslib.DBus.call_method
pystemd.dbusexc.DBusInvalidArgsError: [err -22]: Unit name serial-getty@.service is not valid.

If we do:

from pystemd.systemd1 import Unit

u = Unit('serial-getty@.service')
u.load()

https://github.com/facebookincubator/pystemd/blob/0.5.0/pystemd/dbuslib.pyx#L437.

Note that I'm still using version 0.5.0 since 0.6.0 has not been released yet. Would this be something that this upcoming version would address or am I missing something?

aleivag commented 5 years ago

hi @dabcoder , thanks for asking this.

so what happen is that template units (units with @ in the name) must have something between the @ and the .unit_type , for instance you see this with regular systemctl

# without unit identifier 
 [~] systemctl show  user@.service -p MainPID
Failed to get properties: Unit name user@.service is neither a valid invocation ID nor unit name.

with unit identifier
[~] systemctl show  user@120.service -p MainPID
MainPID=628

i'm 99% sure that the exception i pass to you is a direct translation from systemd. but will come back to this later with a pystemd example...

did this made sense ?

aleivag commented 5 years ago

ok, so now from the pystemd point of view. since systemd does not recognize names of template units that has not been initialized... neither will pystemd, but.. here is a couple of thing you can do:

In [1]: with pystemd.systemd1.Manager() as m:
    ...:     for unit_info in m.Manager.ListUnitsByPatterns([], ['user@*']):
    ...:         unit = pystemd.systemd1.Unit(unit_info[0], _autoload=True)
    ...:         print(unit.Unit.Names, unit)
    ...: 
[b'user@30.service'] <pystemd.systemd1.unit.Unit object at 0x7f6e824c1588>
[b'user@105.service'] <pystemd.systemd1.unit.Unit object at 0x7f6e824c15c0>
[b'user@42.service'] <pystemd.systemd1.unit.Unit object at 0x7f6e823400f0>

note, for the new version, i will release once i fix a small issue with pystemd.run and systemd 240-241 .. but it should be good to use...

dabcoder commented 5 years ago

Hey @aleivag thanks for looking into this and for suggesting this workaround!

So to put things into context, here's what I am doing:

manager = Manager()
manager.load()

list_units = manager.Manager.ListUnitFiles()

Which gives me a list of tuples as you mentioned in your README:

...
(b'/usr/lib/systemd/system/rhel-domainname.service', b'disabled'),
 (b'/usr/lib/systemd/system/fstrim.timer', b'disabled'),
 (b'/usr/lib/systemd/system/serial-getty@.service', b'disabled'),
 (b'/usr/lib/systemd/system/systemd-user-sessions.service', b'static'),
...

I loop through all these units, then apply Unit() on each of those (without the full path). If I do so on the ones that include an @ symbol, that fails. You're right, this is a translation from what systemd returns too, since systemctl show serial-getty@.service -p MainPID would also return an error as you pointed out.

aleivag commented 5 years ago

hi, @dabcoder :

hey, so if what you want are all the units, you can (and probably should) use ListUnits like this

m = pystemd.systemd1.Manager(_autoload=True)
m.Manager.ListUnits()  

that will return something like:

[
...
(b'serial-getty@getty.service',                                                                                                                                                                                                                              
  b'Serial Getty on getty',                                                                                                                                                                                                                                   
  b'loaded',                                                                                                                                                                                                                                                  
  b'inactive',                                                                                                                                                                                                                                                
  b'dead',                                                                                                                                                                                                                                                    
  b'',                                                                                                                                                                                                                                                        
  b'/org/freedesktop/systemd1/unit/serial_2dgetty_40getty_2eservice',                                                                                                                                                                                         
  0,                                                                                                                                                                                                                                                          
  b'',                                                                                                                                                                                                                                                        
  b'/'),                                                                                                                                                                                                                                                      
 (b'hhvm-warmup.service',                                                                                                                                                                                                                                     
  b'hhvm-warmup.service',                                                                                                                                                                                                                                     
  b'loaded',                                                                                                                                                                                                                                                  
  b'inactive',                                                                                                                                                                                                                                                
  b'dead',                                                                                                                                                                                                                                                    
  b'',                                                                                                                                                                                                                                                        
  b'/org/freedesktop/systemd1/unit/hhvm_2dwarmup_2eservice',                                                                                                                                                                                                  
  0,                                                                                                                                                                                                                                                          
  b'',                                                                                                                                                                                                                                                        
  b'/'),                                                                                                                                                                                                                                                      
 (b'sftp-aleivag-data-users-aleivag-buck\\x2dout.mount',   
...
]

and then take the first element of each tuple to create the unit. what you are asking with ListUnitFiles what unit files exists... that fine, but not all unit files, will be units.

Hope that helps!

notes:

non of this is relevant to your question but still...

next version of pystemd (the one in master) you can do

m = pystemd.systemd1.Manager(_autoload=True)
m.Manager.ListUnits()  

or

m = pystemd.systemd1.Manager(_autoload=True)
m.ListUnits()  

and you can also do

m = pystemd.systemd1.Manager(_autoload=True)

or

with pystemd.systemd1.Manager() as m:
   pass

instead of

m = pystemd.systemd1.Manager(_autoload=True)
m.load()
dabcoder commented 5 years ago

Hey @aleivag, thanks for following up!

what you are asking with ListUnitFiles what unit files exists... that fine, but not all unit files, will be units.

I see, thanks for clarifying that.

you can (and probably should) use ListUnits

Yes, that works. I'll close this issue then, thanks again! (I might need to open a new one for GetProcesses() but it'll be unrelated to this one.