summaryrefslogtreecommitdiff
path: root/qga/commands-posix.c
diff options
context:
space:
mode:
Diffstat (limited to 'qga/commands-posix.c')
-rw-r--r--qga/commands-posix.c60
1 files changed, 60 insertions, 0 deletions
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index 915df9ed90..ba06be4c86 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -15,6 +15,7 @@
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <dirent.h>
+#include <utmpx.h>
#include "qga/guest-agent-core.h"
#include "qga-qmp-commands.h"
#include "qapi/qmp/qerror.h"
@@ -2517,3 +2518,62 @@ void ga_command_state_init(GAState *s, GACommandState *cs)
ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup);
#endif
}
+
+#define QGA_MICRO_SECOND_TO_SECOND 1000000
+
+static double ga_get_login_time(struct utmpx *user_info)
+{
+ double seconds = (double)user_info->ut_tv.tv_sec;
+ double useconds = (double)user_info->ut_tv.tv_usec;
+ useconds /= QGA_MICRO_SECOND_TO_SECOND;
+ return seconds + useconds;
+}
+
+GuestUserList *qmp_guest_get_users(Error **err)
+{
+ GHashTable *cache = NULL;
+ GuestUserList *head = NULL, *cur_item = NULL;
+ struct utmpx *user_info = NULL;
+ gpointer value = NULL;
+ GuestUser *user = NULL;
+ GuestUserList *item = NULL;
+ double login_time = 0;
+
+ cache = g_hash_table_new(g_str_hash, g_str_equal);
+ setutxent();
+
+ for (;;) {
+ user_info = getutxent();
+ if (user_info == NULL) {
+ break;
+ } else if (user_info->ut_type != USER_PROCESS) {
+ continue;
+ } else if (g_hash_table_contains(cache, user_info->ut_user)) {
+ value = g_hash_table_lookup(cache, user_info->ut_user);
+ user = (GuestUser *)value;
+ login_time = ga_get_login_time(user_info);
+ /* We're ensuring the earliest login time to be sent */
+ if (login_time < user->login_time) {
+ user->login_time = login_time;
+ }
+ continue;
+ }
+
+ item = g_new0(GuestUserList, 1);
+ item->value = g_new0(GuestUser, 1);
+ item->value->user = g_strdup(user_info->ut_user);
+ item->value->login_time = ga_get_login_time(user_info);
+
+ g_hash_table_insert(cache, item->value->user, item->value);
+
+ if (!cur_item) {
+ head = cur_item = item;
+ } else {
+ cur_item->next = item;
+ cur_item = item;
+ }
+ }
+ endutxent();
+ g_hash_table_destroy(cache);
+ return head;
+}