I'm trying to get a list of the system users in Vala, but all I get is an empty list. As Vala documentation is quite simple, I dont know how to solve this. This is what I'm trying:
var users_list = Act.UserManager.get_default ().list_users ();
It looks as though the UserManager
doesn't have the data available when it is created. The data is only available when the is_loaded
property is true.
In GLib a notify
signal can be emitted when a property changes. So we will take advantage of that in the following working example:
int main () {
var loop = new EventLoop ();
var manager = new UserManager (loop);
if (!manager.is_running) {
print ("AccountsService is not running\n");
return 1;
}
loop.run ();
return 0;
}
class UserManager {
private Act.UserManager manager;
private EventLoop loop;
public bool is_running {
get { return !manager.no_service (); }
}
public UserManager (EventLoop event_loop) {
loop = event_loop;
manager = Act.UserManager.get_default ();
manager.notify["is-loaded"].connect( this.loaded );
}
void loaded (ParamSpec property) {
print (@"Property \"$(property.name)\" has changed\n");
this.print_users ();
this.loop.quit ();
}
void print_users () {
if (!manager.is_loaded) { return; }
print ("%-20s | %-20s\n", "User name", "Logged In Time");
foreach (var user in manager.list_users ()) {
print ("%-20s | %-20s\n",
user.user_name,
new DateTime.from_unix_local(user.login_time).to_string()
);
}
}
}
class EventLoop {
private MainLoop loop;
public EventLoop () {
loop = new MainLoop ();
}
public void run() {
this.loop.run ();
}
public void quit() {
Idle.add (()=> {
this.loop.quit ();
return Source.REMOVE;
});
}
}
The example creates a UserManager
class to wrap the AccountsService UserManager. It is assumed that the user manager is never loaded when it is first returned by Act.UserManager.get_default ()
so as part of the constructor a callback is set up when the is_loaded
property changes. This is the line:
manager.notify["is-loaded"].connect( this.loaded );
The manager
has a notify signal that is emitted when any property changes. The example uses a signal detail to only get fired when the is-loaded
property changes. For some reason this uses a dash in its name, rather than the underscore. I couldn't find any documentation on why that is. With a notify
signal the callback can take a ParamSpec
as an argument. This was used to find details of the property that changed, but in the example is no longer necessary because the "is-loaded"
signal detail is used.
The example also creates an EventLoop
class that acts as a wrapper around GLib's MainLoop
. UserManager
has EventLoop
as a dependency so the event loop can quit and the program finish.
Another approach would be to use the org.freedesktop.Accounts
D-Bus service directly from Vala.