【问题标题】:Why does accountsservice always return an empty user list?为什么accountsservice总是返回一个空的用户列表?
【发布时间】:2017-06-19 10:10:18
【问题描述】:

我正在尝试获取 Vala 中的系统用户列表,但我得到的只是一个空列表。由于 Vala 文档非常简单,我不知道如何解决这个问题。这就是我正在尝试的:

var users_list = Act.UserManager.get_default ().list_users ();

【问题讨论】:

  • accountservice 库的源 tarbar 中有其他文档。点击首页“下载版本”:freedesktop.org/wiki/Software/AccountsService
  • accountsservice 包包含一个可以从终端运行的二进制文件。你可能想先运行它,看看 DBus 接口是否真的在你的系统上工作。
  • 我查看了源代码中的文档,但看起来几乎相同。这似乎是我应该使用的代码。如何检查该二进制文件的名称?
  • 抱歉,没有用户二进制文件。我的印象是,因为有一个二进制包,但是那个不包含用户二进制。
  • 需要注意的一点是:“返回用户管理器单例实例。如果用户列表尚未加载,则调用此函数将自动加载用户列表。将设置“is-loaded”属性当用户完成加载时为 TRUE,然后可以调用 act_user_manager_list_users()。"

标签: linux vala accountsservice


【解决方案1】:

看起来UserManager 在创建时没有可用数据。数据仅在is_loaded 属性为 true 时可用。

在 GLib 中,可以在属性更改时发出 notify 信号。因此,我们将在以下工作示例中利用这一点:

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;
        });
    }
}

该示例创建一个UserManager 类来包装AccountsService UserManager。假定用户管理器在Act.UserManager.get_default () 首次返回时从未加载,因此作为构造函数的一部分,当is_loaded 属性更改时会设置回调。这是一行:

manager.notify["is-loaded"].connect( this.loaded );

manager 有一个通知信号,当任何属性更改时都会发出该信号。该示例使用信号详细信息仅在 is-loaded 属性更改时触发。出于某种原因,它的名称中使用了破折号,而不是下划线。我找不到任何关于为什么会这样的文档。使用notify 信号,回调可以将ParamSpec 作为参数。这用于查找已更改属性的详细信息,但在示例中不再需要,因为使用了 "is-loaded" 信号详细信息。

该示例还创建了一个 EventLoop 类,该类充当 GLib 的 MainLoop 的包装器。 UserManager 具有 EventLoop 作为依赖项,因此事件循环可以退出并且程序结束。

另一种方法是直接从 Vala 使用 org.freedesktop.Accounts D-Bus 服务。

【讨论】:

  • 这很好用,但为什么在 is_loaded 更改之前使用 while 循环不起作用?
  • AccountsService 是org.freedesktop.Accounts D-Bus 服务的客户端。 D-Bus 的 GLib 实现利用 GLib 的MainLoop 进行基于事件的编程。所以我认为你需要让程序返回主循环,这样它才能接收来自 D-Bus 服务的消息。使用while 循环不会这样做。
  • 这就是为什么我建议使用 GLib.Timeout 而不是紧的 while 循环。这个解决方案当然更好:)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-05
  • 1970-01-01
  • 2017-04-13
  • 1970-01-01
  • 2019-12-05
  • 2019-05-28
相关资源
最近更新 更多