VirtualBox

Changeset 101233 in vbox for trunk/src/VBox/Additions/common


Ignore:
Timestamp:
Sep 22, 2023 7:54:00 AM (20 months ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
159216
Message:

Additions/VBoxService/VBoxServiceVMInfo.cpp: implement user detection via systemd-logind "org.freedesktop.login1" DBus protocol

Also cleaned up old ConsoleKit code bugs & todos. bugref:6492

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Additions/common/VBoxService/VBoxServiceVMInfo.cpp

    r98103 r101233  
    165165/** @name ConsoleKit defines (taken from 0.4.5).
    166166 * @{ */
    167 # define CK_NAME                "org.freedesktop.ConsoleKit"
    168 # define CK_PATH                "/org/freedesktop/ConsoleKit"
     167# define CK_NAME                "org.freedesktop.ConsoleKit"            /* unused */
     168# define CK_PATH                "/org/freedesktop/ConsoleKit"           /* unused */
    169169# define CK_INTERFACE           "org.freedesktop.ConsoleKit"
    170170# define CK_MANAGER_PATH        "/org/freedesktop/ConsoleKit/Manager"
    171171# define CK_MANAGER_INTERFACE   "org.freedesktop.ConsoleKit.Manager"
    172 # define CK_SEAT_INTERFACE      "org.freedesktop.ConsoleKit.Seat"
     172# define CK_SEAT_INTERFACE      "org.freedesktop.ConsoleKit.Seat"       /* unused */
    173173# define CK_SESSION_INTERFACE   "org.freedesktop.ConsoleKit.Session"
     174/** @} */
     175/** @name systemd-logind defines
     176 * @{ */
     177# define SYSTEMD_LOGIN_INTERFACE           "org.freedesktop.login1"
     178# define SYSTEMD_LOGIN_PATH                "/org/freedesktop/login1"
     179# define SYSTEMD_LOGIN_MANAGER_INTERFACE   "org.freedesktop.login1.Manager"
     180# define SYSTEMD_LOGIN_SESSION_INTERFACE   "org.freedesktop.login1.Session"
    174181/** @} */
    175182#endif
     
    538545#if defined(VBOX_WITH_DBUS) && defined(RT_OS_LINUX) /* Not yet for Solaris/FreeBSB. */
    539546/*
    540  * Simple wrapper to work around compiler-specific va_list madness.
     547 * Simple wrappers to work around compiler-specific va_list madness.
    541548 */
    542549static dbus_bool_t vboxService_dbus_message_get_args(DBusMessage *message, DBusError *error, int first_arg_type, ...)
     
    548555    return ret;
    549556}
     557
     558static dbus_bool_t vboxService_dbus_message_append_args(DBusMessage *message, int first_arg_type, ...)
     559{
     560    va_list va;
     561    va_start(va, first_arg_type);
     562    dbus_bool_t ret = dbus_message_append_args_valist(message, first_arg_type, va);
     563    va_end(va);
     564    return ret;
     565}
     566
     567#ifndef DBUS_TYPE_VARIANT
     568#define DBUS_TYPE_VARIANT       ((int) 'v')
    550569#endif
    551 
     570/*
     571 * Wrapper to dig values out of dbus replies, which are contained in
     572 * a 'variant' and must be iterated into twice.
     573 *
     574 * Returns true if it thinks it got a value; false if not.
     575 *
     576 * This does various error checking so the caller can skip it:
     577 *   - whether a DBusError is set
     578 *   - whether the DBusMessage is valid
     579 *   - whether we actually got a 'variant'
     580 *   - whether we got the type the caller's looking for
     581 */
     582static bool vboxService_dbus_unpack_variant_reply(DBusError *error, DBusMessage *pReply, char pType, void *pValue) {
     583    if (dbus_error_is_set(error)) {
     584        VGSvcError("dbus_unpack_variant_reply: dbus returned error '%s'\n", error->message);
     585        dbus_error_free(error);
     586    } else if (pReply) {
     587        DBusMessageIter iterMsg;
     588        int             iterType;
     589        dbus_message_iter_init(pReply, &iterMsg);
     590        iterType = dbus_message_iter_get_arg_type(&iterMsg);
     591        if (iterType == DBUS_TYPE_VARIANT) {
     592            DBusMessageIter iterValueMsg;
     593            int             iterValueType;
     594            dbus_message_iter_recurse(&iterMsg, &iterValueMsg);
     595            iterValueType = dbus_message_iter_get_arg_type(&iterValueMsg);
     596            if (iterValueType == pType) {
     597                dbus_message_iter_get_basic(&iterValueMsg, pValue);
     598                return true;
     599            }
     600        }
     601    }
     602    return false;
     603}
     604
     605/*
     606 * Wrapper to NULL out the DBusMessage pointer while discarding it.
     607 * DBus API is multi-threaded and can have multiple concurrent accessors.
     608 * Our use here is single-threaded and can never have multiple accessors.
     609 */
     610static void vboxService_dbus_message_discard(DBusMessage **ppMsg)
     611{
     612    if (ppMsg && *ppMsg) {
     613#if defined(ICKY_REFCOUNT_GRUBBING)
     614        {
     615            /* The DBusMessage Refcount private field isn't externally accessible.
     616             * We want to check it for debug / assert purposes.
     617             */
     618            uint32_t *pcRefCnt = (uint32_t *)(*ppMsg);
     619            if (*p == 0) {
     620                VGSvcVerbose(1, "dbus_message_discard: unref %p whose refcnt is 0 (expected 1) -- this would have been a double-free\n", ppMsg);
     621                *ppMsg = NULL;
     622                return;
     623            } else if (*p != 1) {
     624                VGSvcVerbose(1, "dbus_message_discard: unref %p whose refcnt is %d (expected 1)\n", ppMsg, *p);
     625            }
     626        }
     627#endif /* ICKY_REFCOUNT_GRUBBING */
     628        dbus_message_unref(*ppMsg);
     629        *ppMsg = NULL;
     630    }
     631}
     632#endif
     633
     634
     635/*
     636 * Add a user to the list of active users (while ignoring duplicates
     637 * and dynamically maintaining the list storage)
     638 */
     639#define USER_LIST_CHUNK_SIZE 32
     640static uint32_t cUsersInList;
     641static uint32_t cListSize;
     642static char **papszUsers;
     643
     644static void vgsvcVMInfoAddUserToList(const char *name, const char *src)
     645{
     646    int rc;
     647    bool fFound = false;
     648    for (uint32_t idx = 0; idx < cUsersInList && !fFound; idx++)
     649        fFound = strncmp(papszUsers[idx], name, 32) == 0;
     650    VGSvcVerbose(5, "LoggedInUsers: Asked to add user '%s' from '%s' to list (already in list = %lu)\n", name, src, fFound);
     651    if (!fFound)
     652    {
     653        if (cUsersInList + 1 > cListSize)
     654        {
     655            VGSvcVerbose(5, "LoggedInUsers: increase user list size from %lu to %lu\n", cListSize, cListSize + USER_LIST_CHUNK_SIZE);
     656            cListSize += USER_LIST_CHUNK_SIZE;
     657            void *pvNew = RTMemRealloc(papszUsers, cListSize * sizeof(char*));
     658            AssertReturnVoidStmt(pvNew, cListSize -= USER_LIST_CHUNK_SIZE);
     659            papszUsers = (char **)pvNew;
     660        }
     661        VGSvcVerbose(4, "LoggedInUsers: Adding user '%s' from '%s' to list (size = %lu, count = %lu)\n", name, src, cListSize, cUsersInList);
     662        rc = RTStrDupEx(&papszUsers[cUsersInList], name);
     663        if (!RT_FAILURE(rc))
     664            cUsersInList++;
     665    }
     666}
    552667
    553668/**
     
    558673    int rc;
    559674    char *pszUserList = NULL;
    560     uint32_t cUsersInList = 0;
     675
     676    cUsersInList = 0;
    561677
    562678#ifdef RT_OS_WINDOWS
     
    580696    setutxent();
    581697    utmpx *ut_user;
    582     uint32_t cListSize = 32;
     698    cListSize = USER_LIST_CHUNK_SIZE;
    583699
    584700    /* Allocate a first array to hold 32 users max. */
    585     char **papszUsers = (char **)RTMemAllocZ(cListSize * sizeof(char *));
     701    papszUsers = (char **)RTMemAllocZ(cListSize * sizeof(char *));
    586702    if (papszUsers)
    587703        rc = VINF_SUCCESS;
     
    600716                     ut_user->ut_user, ut_user->ut_type, ut_user->ut_pid, ut_user->ut_session);
    601717# endif
    602         if (cUsersInList > cListSize)
    603         {
    604             cListSize += 32;
    605             void *pvNew = RTMemRealloc(papszUsers, cListSize * sizeof(char*));
    606             AssertBreakStmt(pvNew, cListSize -= 32);
    607             papszUsers = (char **)pvNew;
    608         }
    609718
    610719        /* Make sure we don't add user names which are not
     
    612721        if (ut_user->ut_type == USER_PROCESS) /* Regular user process. */
    613722        {
    614             bool fFound = false;
    615             for (uint32_t i = 0; i < cUsersInList && !fFound; i++)
    616                 fFound = strncmp(papszUsers[i], ut_user->ut_user, sizeof(ut_user->ut_user)) == 0;
    617 
    618             if (!fFound)
    619             {
    620                 VGSvcVerbose(4, "Adding user '%s' (type: %d) to list\n", ut_user->ut_user, ut_user->ut_type);
    621 
    622                 rc = RTStrDupEx(&papszUsers[cUsersInList], (const char *)ut_user->ut_user);
    623                 if (RT_FAILURE(rc))
    624                     break;
    625                 cUsersInList++;
    626             }
     723            vgsvcVMInfoAddUserToList(ut_user->ut_user, "utmpx");
    627724        }
    628725    }
     
    636733    if (RT_SUCCESS(rc2))
    637734    {
     735        /* Handle desktop sessions using systemd-logind. */
     736        VGSvcVerbose(4, "Checking systemd-logind sessions ...\n");
     737        fHaveLibDbus = true;
     738        dbus_error_init(&dbErr);
     739        pConnection = dbus_bus_get(DBUS_BUS_SYSTEM, &dbErr);
     740    }
     741
     742    if (   pConnection
     743        && !dbus_error_is_set(&dbErr))
     744    {
     745// TODO: is there some Less Horrible Way(tm) to access dbus?
     746        /* Get all available sessions. */
     747        /* like `busctl call org.freedesktop.login1 /org/freedesktop/login1 org.freedesktop.login1.Manager ListSessions` */
     748        DBusMessage *pMsgSessions = dbus_message_new_method_call(SYSTEMD_LOGIN_INTERFACE,
     749                                                                 SYSTEMD_LOGIN_PATH,
     750                                                                 SYSTEMD_LOGIN_MANAGER_INTERFACE,
     751                                                                 "ListSessions");
     752        if (   pMsgSessions
     753            && dbus_message_get_type(pMsgSessions) == DBUS_MESSAGE_TYPE_METHOD_CALL)
     754        {
     755            DBusMessage *pReplySessions = dbus_connection_send_with_reply_and_block(pConnection,
     756                                                                                    pMsgSessions, 30 * 1000 /* 30s timeout */,
     757                                                                                    &dbErr);
     758            if (   pReplySessions
     759                && !dbus_error_is_set(&dbErr))
     760            {
     761                /* dbus_message_new_method_call() returns a DBusMessage, which we must iterate to get the returned value */
     762                DBusMessageIter messageIterMsg;
     763                int             messageIterType;
     764                dbus_message_iter_init(pReplySessions, &messageIterMsg);
     765                while ((messageIterType = dbus_message_iter_get_arg_type (&messageIterMsg)) != DBUS_TYPE_INVALID) {
     766                    if (messageIterType == DBUS_TYPE_ARRAY) {
     767                        /* "ListSessions" returns an array, which we must iterate to get the array elements */
     768                        DBusMessageIter arrayIterMsg;
     769                        int             arrayIterType;
     770                        dbus_message_iter_recurse(&messageIterMsg, &arrayIterMsg);
     771                        while ((arrayIterType = dbus_message_iter_get_arg_type (&arrayIterMsg)) != DBUS_TYPE_INVALID) {
     772                            if (arrayIterType == DBUS_TYPE_STRUCT) {
     773                                /* The array elements are structs, which we must iterate to get the struct elements */
     774                                DBusMessageIter structIterMsg;
     775                                int             structIterType;
     776                                dbus_message_iter_recurse(&arrayIterMsg, &structIterMsg);
     777                                while ((structIterType = dbus_message_iter_get_arg_type (&structIterMsg)) != DBUS_TYPE_INVALID) {
     778                                    if (structIterType == DBUS_TYPE_OBJECT_PATH) {
     779                                        /* We are interested only in the "object path" struct element */
     780                                        const char *objectPath;
     781                                        dbus_message_iter_get_basic(&structIterMsg, &objectPath);
     782                                        const char *pInterface = SYSTEMD_LOGIN_SESSION_INTERFACE;
     783                                        /* Create and send a new dbus query asking for that session's details */
     784                                        DBusMessage *pMsgSession = dbus_message_new_method_call(SYSTEMD_LOGIN_INTERFACE,
     785                                                                                                objectPath,
     786                                                                                                "org.freedesktop.DBus.Properties",
     787                                                                                                "Get");
     788                                        if (   pMsgSession
     789                                            && dbus_message_get_type(pMsgSession) == DBUS_MESSAGE_TYPE_METHOD_CALL) {
     790                                            const char *pPropertyActive = "Active";
     791                                            vboxService_dbus_message_append_args(pMsgSession,
     792                                                                                 DBUS_TYPE_STRING, &pInterface,
     793                                                                                 DBUS_TYPE_STRING, &pPropertyActive,
     794                                                                                 DBUS_TYPE_INVALID, 0);
     795                                            /* like `busctl get-property org.freedesktop.login1 %s org.freedesktop.login1.Session Active` %(objectPath) */
     796                                            DBusMessage *pReplySession = dbus_connection_send_with_reply_and_block(
     797                                                                             pConnection,
     798                                                                             pMsgSession,
     799                                                                             -1,
     800                                                                             &dbErr);
     801                                            int sessionPropertyActiveValue;
     802                                            if (   vboxService_dbus_unpack_variant_reply(
     803                                                       &dbErr,
     804                                                       pReplySession,
     805                                                       DBUS_TYPE_BOOLEAN,
     806                                                       &sessionPropertyActiveValue)
     807                                                && sessionPropertyActiveValue) {
     808                                                DBusMessage *pMsgSession2 = dbus_message_new_method_call(SYSTEMD_LOGIN_INTERFACE,
     809                                                                                                         objectPath,
     810                                                                                                         "org.freedesktop.DBus.Properties",
     811                                                                                                         "Get");
     812                                                const char *pPropertyName = "Name";
     813                                                if (   pMsgSession2
     814                                                    && dbus_message_get_type(pMsgSession2) == DBUS_MESSAGE_TYPE_METHOD_CALL) {
     815                                                    vboxService_dbus_message_append_args(pMsgSession2,
     816                                                                                         DBUS_TYPE_STRING, &pInterface,
     817                                                                                         DBUS_TYPE_STRING, &pPropertyName,
     818                                                                                         DBUS_TYPE_INVALID, 0);
     819                                                    /* like `busctl get-property org.freedesktop.login1 %s org.freedesktop.login1.Session Name` %(objectPath) */
     820                                                    DBusMessage *pReplyName = dbus_connection_send_with_reply_and_block(
     821                                                                                 pConnection,
     822                                                                                 pMsgSession2,
     823                                                                                 -1,
     824                                                                                 &dbErr);
     825                                                    const char *sessionPropertyNameValue;
     826                                                    if (   vboxService_dbus_unpack_variant_reply(
     827                                                               &dbErr,
     828                                                               pReplyName,
     829                                                               DBUS_TYPE_STRING,
     830                                                               &sessionPropertyNameValue)
     831                                                        && sessionPropertyNameValue)
     832                                                        vgsvcVMInfoAddUserToList(sessionPropertyNameValue, "systemd-logind");
     833                                                    vboxService_dbus_message_discard(&pReplyName);
     834                                                }
     835                                                vboxService_dbus_message_discard(&pMsgSession2);
     836                                            }
     837                                            vboxService_dbus_message_discard(&pReplySession);
     838                                        }
     839                                        vboxService_dbus_message_discard(&pMsgSession);
     840                                    }
     841                                    dbus_message_iter_next (&structIterMsg);
     842                                }
     843                            }
     844                            dbus_message_iter_next (&arrayIterMsg);
     845                        }
     846                    }
     847                    dbus_message_iter_next (&messageIterMsg);
     848                }
     849                vboxService_dbus_message_discard(&pReplySessions);
     850            }
     851        }
     852        else
     853        {
     854            static int s_iBitchedAboutSystemdLogind = 0;
     855            if (s_iBitchedAboutSystemdLogind < 3)
     856            {
     857                s_iBitchedAboutSystemdLogind++;
     858                VGSvcError("Unable to invoke systemd-logind (%d/3) -- maybe not installed / used? Error: %s\n",
     859                           s_iBitchedAboutSystemdLogind,
     860                           dbus_error_is_set(&dbErr) ? dbErr.message : "No error information available");
     861            }
     862        }
     863
     864        vboxService_dbus_message_discard(&pMsgSessions);
     865        if (dbus_error_is_set(&dbErr))
     866        {
     867            dbus_error_free(&dbErr);
     868        }
     869    }
     870    if (RT_SUCCESS(rc2))
     871    {
    638872        /* Handle desktop sessions using ConsoleKit. */
    639873        VGSvcVerbose(4, "Checking ConsoleKit sessions ...\n");
    640874        fHaveLibDbus = true;
    641875        dbus_error_init(&dbErr);
     876        /* TODO: should this be dbus_connection_open() (and below, dbus_connection_unref())? */
    642877        pConnection = dbus_bus_get(DBUS_BUS_SYSTEM, &dbErr);
    643878    }
     
    647882    {
    648883        /* Get all available sessions. */
    649 /** @todo r=bird: What's the point of hardcoding things here when we've taken the pain of defining CK_XXX constants at the top of the file (or vice versa)? */
    650         DBusMessage *pMsgSessions = dbus_message_new_method_call("org.freedesktop.ConsoleKit",
    651                                                                  "/org/freedesktop/ConsoleKit/Manager",
    652                                                                  "org.freedesktop.ConsoleKit.Manager",
     884        DBusMessage *pMsgSessions = dbus_message_new_method_call(CK_INTERFACE,
     885                                                                 CK_MANAGER_PATH,
     886                                                                 CK_MANAGER_INTERFACE,
    653887                                                                 "GetSessions");
    654888        if (   pMsgSessions
     
    677911                        /* Only respect active sessions .*/
    678912                        bool fActive = false;
    679                         DBusMessage *pMsgSessionActive = dbus_message_new_method_call("org.freedesktop.ConsoleKit",
     913                        DBusMessage *pMsgSessionActive = dbus_message_new_method_call(CK_INTERFACE,
    680914                                                                                      *ppszCurSession,
    681                                                                                       "org.freedesktop.ConsoleKit.Session",
     915                                                                                      CK_SESSION_INTERFACE,
    682916                                                                                      "IsActive");
    683917                        if (   pMsgSessionActive
     
    701935                                }
    702936
    703                                 if (pReplySessionActive)
    704                                     dbus_message_unref(pReplySessionActive);
    705937                            }
    706 
    707                             if (pMsgSessionActive)
    708                                 dbus_message_unref(pMsgSessionActive);
     938                            /* TODO: clean up if &dbErr */
     939                            vboxService_dbus_message_discard(&pReplySessionActive);
     940
     941                            vboxService_dbus_message_discard(&pMsgSessionActive);
    709942                        }
    710943
     
    714947                        /* *ppszCurSession now contains the object path
    715948                         * (e.g. "/org/freedesktop/ConsoleKit/Session1"). */
    716                         DBusMessage *pMsgUnixUser = dbus_message_new_method_call("org.freedesktop.ConsoleKit",
     949                        DBusMessage *pMsgUnixUser = dbus_message_new_method_call(CK_INTERFACE,
    717950                                                                                 *ppszCurSession,
    718                                                                                  "org.freedesktop.ConsoleKit.Session",
     951                                                                                 CK_SESSION_INTERFACE,
    719952                                                                                 "GetUnixUser");
    720953                        if (   fActive
     
    737970                                    dbus_message_iter_get_basic(&itMsg, &uid);
    738971
    739                                     /** @todo Add support for getting UID_MIN (/etc/login.defs on
    740                                      *        Debian). */
    741                                     uint32_t uid_min = 1000;
    742 
    743972                                    /* Look up user name (realname) from uid. */
    744973                                    setpwent();
     
    747976                                        && ppwEntry->pw_name)
    748977                                    {
    749                                         if (ppwEntry->pw_uid >= uid_min /* Only respect users, not daemons etc. */)
    750                                         {
    751978                                            VGSvcVerbose(4, "ConsoleKit: session '%s' -> %s (uid: %RU32)\n",
    752979                                                         *ppszCurSession, ppwEntry->pw_name, uid);
    753 
    754                                             bool fFound = false;
    755                                             for (uint32_t i = 0; i < cUsersInList && !fFound; i++)
    756                                                 fFound = strcmp(papszUsers[i], ppwEntry->pw_name) == 0;
    757 
    758                                             if (!fFound)
    759                                             {
    760                                                 VGSvcVerbose(4, "ConsoleKit: adding user '%s' to list\n", ppwEntry->pw_name);
    761 
    762                                                 rc = RTStrDupEx(&papszUsers[cUsersInList], (const char *)ppwEntry->pw_name);
    763                                                 if (RT_FAILURE(rc))
    764                                                     break;
    765                                                 cUsersInList++;
    766                                             }
    767                                         }
    768                                         /* else silently ignore the user */
     980                                            vgsvcVMInfoAddUserToList(ppwEntry->pw_name, "ConsoleKit");
    769981                                    }
    770982                                    else
     
    774986                                    AssertMsgFailed(("ConsoleKit: GetUnixUser returned a wrong argument type\n"));
    775987                            }
    776 
    777                             if (pReplyUnixUser)
    778                                 dbus_message_unref(pReplyUnixUser);
     988                            /* TODO: clean up if &dbErr */
     989
     990                            vboxService_dbus_message_discard(&pReplyUnixUser);
    779991                        }
    780992                        else if (fActive) /* don't bitch about inactive users */
     
    7901002                        }
    7911003
    792                         if (pMsgUnixUser)
    793                             dbus_message_unref(pMsgUnixUser);
     1004                        vboxService_dbus_message_discard(&pMsgUnixUser);
    7941005                    }
    7951006
     
    8001011                               dbus_message_get_type(pMsgSessions),
    8011012                               dbus_error_is_set(&dbErr) ? dbErr.message : "No error information available");
    802                 dbus_message_unref(pReplySessions);
    803             }
    804 
    805             if (pMsgSessions)
    806             {
    807                 dbus_message_unref(pMsgSessions);
    808                 pMsgSessions = NULL;
     1013                vboxService_dbus_message_discard(&pReplySessions);
    8091014            }
    8101015        }
     
    8211026        }
    8221027
    823         if (pMsgSessions)
    824             dbus_message_unref(pMsgSessions);
     1028        vboxService_dbus_message_discard(&pMsgSessions);
    8251029    }
    8261030    else
     
    8401044#  endif /* RT_OS_LINUX */
    8411045# endif /* VBOX_WITH_DBUS */
    842 
    843     /** @todo Fedora/others: Handle systemd-loginctl. */
    8441046
    8451047    /* Calc the string length. */
Note: See TracChangeset for help on using the changeset viewer.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette