Session, Desktops and login screen

Coming from windows development, I'm trying to understand macOS architecture and how to do certain things. I've already read the Root and Login Sessions AND Service and Daemons AND User Switch Notifications documentation so will frame the questions accordingly.

  1. On Windows, there's a concept of User Sessions, each of which contain One or more WindowStations, each of which contain One or more Desktops. Each user gets at least 3 desktops (e.g. Login/Lock/UAC, Screensaver, and default desktop). From what I understand about macOS, it only has Sessions and then a single Desktop. Is that correct? i.e. same display surface is used to display user's desktop, screensaver, sudo prompt and lock screen?

  2. What about login screen? Does each user get its own login screen process/window running in their session? or is there a common login screen for all users running in one particular session (root?). How does Fast User switching effect login screen?

  3. In a daemon, is it possible to get active console session ID? console meaning the session being displayed on the monitor, whether its login screen, lock screen, user's desktop etc.

  4. In a daemon, is it possible to get session switch notifications? E.g. user logged-in and now their desktop is being displayed, user logged-out and now we're back on login screen, or user switched to another user (Fast User switching). How do I get notification of such events in daemon?

  5. If no user is logged in which session is pre-login agent running in? and after login does the session ID assigned to pre-login agent stay the same and user's session is assigned a new session ID?

  6. Is there always one and only one pre-login agent running?

  7. Is it possible to launch pre-login agent and user agents on-demand with custom commandline arguments from a daemon?

Answered by DTS Engineer in 803061022
I'm trying to understand macOS architecture

If you haven’t already, I recommend that you read through Technote 2083 Daemons and Agents. While it’s old, and could really do with an update, the core concepts it describes are still valid.

From what I understand about macOS, it only has Sessions and then a single Desktop. Is that correct?

Yes. The terminology is very different but:

  • Each is limited to a single GUI login session.

  • That session has an associated window server session [1].

  • That window server session is used for all GUI work related to that login session. This includes, for example, screen saver unlock.

There’s an interesting wrinkle here: GUI authorisation plug-in code runs as a different user, _securityagent, but has some access to that window server session.

What about login screen? Does each user get its own login screen process/window running in their session?

I don’t see how that question makes sense. The login screen lets you choose the user, so at least some part of it must necessarily run in some user-independent context.

The pre-login session is implemented using a combination of processes running as either root or _securityagent.

How does Fast User switching effect login screen?

When you explicitly navigate to the login window itself, it switches to the pre-login session, spinning it up if necessary.

You have to be careful here. The exact relationship between pre-login sessions, GUI login sessions, and window server sessions is considered an implementation detail. Those implementation details are visible if you look hard enough — by installing a pre-login agent, or via Endpoint Security — but you can’t assume that the current behaviour is how it’ll always work. For example, if you fast user switch from user A to user B, there may or may not be a pre-login session spun up in between.

In a daemon, is it possible to get active console session ID?

Yes, but it’s best to avoid doing that. This question is predicated on the assumption that there’s only a single GUI login session visible on screen at a time. That’s not true. With screen sharing it’s possible to have multiple GUI login sessions active simultaneously [2].

FYI, the best way to get the current console user from a daemon is SCDynamicStoreCopyConsoleUser. This is based on the System Configuration framework dynamic store, which has built-in notification support, so you can be notified of changes as well.

In a daemon, is it possible to get session switch notifications?

Yes and no. You can get notifications from System Configuration framework, as I mentioned above, but that has the same limiting assumption.

There are two good ways to learn about login sessions:

  • If you’re doing security stuff, Endpoint Security has relevant events.

  • Otherwise, your best option is to install a global launchd agent. The system will start an instance of this as sessions come and go, and each instance can use check-in with your daemon (typically using XPC) and notify it of any relevant state updates. I talk about this in TN2083.

IMPORTANT This daemon-with-zero-or-more-helper-agents architecture has stood the test of time. It worked correctly on macOS 10.5 and continues to work correctly on macOS 15. I’ve helped many folks who’ve tried to take shortcuts here and then found that their code broke as the system evolved.

If no user is logged in which session is pre-login agent running in?

A pre-login session.

after login does the session ID assigned to pre-login agent stay the same and user's session is assigned a new session ID?

Which session ID? There are various different ways to identify a session on macOS, and the exact behaviour here is changes depending on which one you use.

If you use the daemon-with-zero-or-more-helper-agents architecture, you rarely need to worry about session IDs, because the XPC connection between the agent and your daemon identifies the session.

Is there always one and only one pre-login agent running?

No.

It’s common for no instances of your pre-login agent to be running, for example, once the user has logged in.

I think it’s safe to assume that there’ll be at most one instance of your pre-login agent running. I think. But, honestly, I’d try to avoid writing code in a way that assumes that. If you use the daemon-with-zero-or-more-helper-agents architecture, you shouldn’t need to make that assumption. Rather, each agent should report its session information to your daemon, which can coordinate things from there.

Is it possible to launch pre-login agent and user agents on-demand … from a daemon?

Yes. It’s tricky, but it can be done.

with custom commandline arguments

No.

Asking this question suggests that your thinking is ‘upside down’. Rather than have the daemon monitor the current sessions and an start agent in each session, have the system start your agents as sessions come and go, and then have those agents ‘check in’ with your daemon.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] That’s an internal concept, not something with a public API.

[2] I always wished that we’d support multiple users on physical consoles. After all, you can plug-in multiple monitors, multiple keyboards and mice, and so on. Sadly, I don’t think that’ll ever happen.

Accepted Answer
I'm trying to understand macOS architecture

If you haven’t already, I recommend that you read through Technote 2083 Daemons and Agents. While it’s old, and could really do with an update, the core concepts it describes are still valid.

From what I understand about macOS, it only has Sessions and then a single Desktop. Is that correct?

Yes. The terminology is very different but:

  • Each is limited to a single GUI login session.

  • That session has an associated window server session [1].

  • That window server session is used for all GUI work related to that login session. This includes, for example, screen saver unlock.

There’s an interesting wrinkle here: GUI authorisation plug-in code runs as a different user, _securityagent, but has some access to that window server session.

What about login screen? Does each user get its own login screen process/window running in their session?

I don’t see how that question makes sense. The login screen lets you choose the user, so at least some part of it must necessarily run in some user-independent context.

The pre-login session is implemented using a combination of processes running as either root or _securityagent.

How does Fast User switching effect login screen?

When you explicitly navigate to the login window itself, it switches to the pre-login session, spinning it up if necessary.

You have to be careful here. The exact relationship between pre-login sessions, GUI login sessions, and window server sessions is considered an implementation detail. Those implementation details are visible if you look hard enough — by installing a pre-login agent, or via Endpoint Security — but you can’t assume that the current behaviour is how it’ll always work. For example, if you fast user switch from user A to user B, there may or may not be a pre-login session spun up in between.

In a daemon, is it possible to get active console session ID?

Yes, but it’s best to avoid doing that. This question is predicated on the assumption that there’s only a single GUI login session visible on screen at a time. That’s not true. With screen sharing it’s possible to have multiple GUI login sessions active simultaneously [2].

FYI, the best way to get the current console user from a daemon is SCDynamicStoreCopyConsoleUser. This is based on the System Configuration framework dynamic store, which has built-in notification support, so you can be notified of changes as well.

In a daemon, is it possible to get session switch notifications?

Yes and no. You can get notifications from System Configuration framework, as I mentioned above, but that has the same limiting assumption.

There are two good ways to learn about login sessions:

  • If you’re doing security stuff, Endpoint Security has relevant events.

  • Otherwise, your best option is to install a global launchd agent. The system will start an instance of this as sessions come and go, and each instance can use check-in with your daemon (typically using XPC) and notify it of any relevant state updates. I talk about this in TN2083.

IMPORTANT This daemon-with-zero-or-more-helper-agents architecture has stood the test of time. It worked correctly on macOS 10.5 and continues to work correctly on macOS 15. I’ve helped many folks who’ve tried to take shortcuts here and then found that their code broke as the system evolved.

If no user is logged in which session is pre-login agent running in?

A pre-login session.

after login does the session ID assigned to pre-login agent stay the same and user's session is assigned a new session ID?

Which session ID? There are various different ways to identify a session on macOS, and the exact behaviour here is changes depending on which one you use.

If you use the daemon-with-zero-or-more-helper-agents architecture, you rarely need to worry about session IDs, because the XPC connection between the agent and your daemon identifies the session.

Is there always one and only one pre-login agent running?

No.

It’s common for no instances of your pre-login agent to be running, for example, once the user has logged in.

I think it’s safe to assume that there’ll be at most one instance of your pre-login agent running. I think. But, honestly, I’d try to avoid writing code in a way that assumes that. If you use the daemon-with-zero-or-more-helper-agents architecture, you shouldn’t need to make that assumption. Rather, each agent should report its session information to your daemon, which can coordinate things from there.

Is it possible to launch pre-login agent and user agents on-demand … from a daemon?

Yes. It’s tricky, but it can be done.

with custom commandline arguments

No.

Asking this question suggests that your thinking is ‘upside down’. Rather than have the daemon monitor the current sessions and an start agent in each session, have the system start your agents as sessions come and go, and then have those agents ‘check in’ with your daemon.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] That’s an internal concept, not something with a public API.

[2] I always wished that we’d support multiple users on physical consoles. After all, you can plug-in multiple monitors, multiple keyboards and mice, and so on. Sadly, I don’t think that’ll ever happen.

FYI, When I mention Session I always mean a UI Session (whether its prelogin or User's) - gotten used to saying just Session because of Windows. I'm working on a screen sharing app so that's why I'm interested in capturing the entire UI pre-login, lock, desktop etc.

That window server session is used for all GUI work related to that login session. This includes, for example, screen saver unlock.

So login screen runs in its own GUI Session (does it get its own GUI Session ID?) and for that Pre-Login Agent is launched and it runs in the context of user _securityagent. After Login the user's GUI session is created and user gets its own Window Server. Everything relative to that User's GUI is displayed in that GUI Session i.e. normal desktop, lock screen and screensaver?

For example, if you fast user switch from user A to user B, there may or may not be a pre-login session spun up in between.

Which GUI session is responsible for displaying UI in this scenario? First user A's GUI session or user B's?

This question is predicated on the assumption that there’s only a single GUI login session visible on screen at a time. That’s not true. With screen sharing it’s possible to have multiple GUI login sessions active simultaneously [2]. FYI, the best way to get the current console user from a daemon is SCDynamicStoreCopyConsoleUser.

AFAIK, there is only one Active GUI session possible on mac. What's the scenario in which multiple GUI login sessions are Active and displaying a UI?

I looked into SCDynamicStoreCopyConsoleUser it returns username, uid, and gid. How do I get GUI session ID for the console user? (for both cases, prelogin and user session)

This daemon-with-zero-or-more-helper-agents architecture has stood the test of time.

That's the approach I'm planning on taking now. My main concern with that is if a user keeps terminating one of the user agents, would the system keep restarting it? If not, my daemon would be blind to what's going on in that user's session - in terms of capturing screen etc.

Also, with this approach of Pre-login & user agents doing the work. Assuming each Agent notifies daemon of the active/current session. Would the system always be in correct 'state' i.e. is there a scenario where my application would miss out on screen capture e.g. fast user switching from user A to user B, etc basically the edge cases. Would at least one Agent (pre-login or User) always be in the Active Window server to capture screen?

I'm working on a screen sharing app

Thanks for that. This is a well-trodden path, one that I’ve helped numerous developers navigate.

Two things up front:

  • I presume you’ve seen the PreLoginAgents sample code. Its read me document mentions a bug (r. 5636091). If you haven’t already done so, please open a DTS code-level support request so I can discuss that bug with your privately.

    IMPORTANT The submission form asks you whether someone asked you to contact DTS. Make sure to answer “Yes” and point to this DevForums thread.

  • macOS 15 (currently a release candidate) has changes such that screen sharing apps need an additional capability. See Persistent Content Capture.

does it get its own GUI Session ID?

You have to define what you mean by “GUI Session ID”. What API are you using that works in terms of session IDs.

So login screen runs in its own GUI Session (does it get its own GUI Session ID?) and for that Pre-Login Agent is launched and it runs in the context of user _securityagent. the user's GUI session is created and user gets its own Window Server. Everything relative to that User's GUI is displayed in that GUI Session … ?

That’s basically right. The only thing to correct is “user gets its own Window Server”. There is but one instance of the window server; it manages all the GUI login sessions.

Last I checked — and this was probably a decade ago — that window server would terminate and relaunch when the last GUI user logged out. However, that’s mostly an implementation detail, except insofar as it’s one of the reasons why your daemon must not connect to the window server.

Which GUI session is responsible for displaying UI in this scenario? First user A's GUI session or user B's?

That’s not specified; I’m pretty sure it’s changed over time.

AFAIK, there is only one Active GUI session possible on mac.

That’s not right. You should explore Apple’s screen sharing features at the user level. If you connect while another user is logged in, you have the option of either starting a new GUI session or joining the existing GUI session.

How do I get GUI session ID for the console user?

Again, I need you define what you mean by that term.

My main concern with that is if a user keeps terminating one of the user agents, would the system keep restarting it?

Yes. If you falls over immediately then launchd can throttle the relaunch, but it will eventually relaunch it.

This is one of those places where the big picture matters. If you’re building a security product you have to think carefully about this possibility. The user might be actively attacking your product, so you have to ‘fail secure’. OTOH, that’s not really a concern for a screen sharing product. It’s clearly working with the cooperation of the user, and you can just let launchd do its thing.

Would at least one Agent (pre-login or User) always be in the Active Window server to capture screen?

Yes. If you configure your agent to run in pre-login and GUI sessions then the system will converge to be running at least once instance of it.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

macOS 15 (currently a release candidate) has changes such that screen sharing apps need an additional capability.

Thanks for the headsup!

You have to define what you mean by “GUI Session ID”. What API are you using that works in terms of session IDs.

SessionGetInfo - Just looked at the documentation, Apple terminology is Security Session ID. Note that I'm only interested in UI sessions, so sessions that have sessionHasGraphicAccess bit set. Is there a way to enumerate all Sessions in daemon and also get their state (i.e. which one is Active)?

You should explore Apple’s screen sharing features at the user level.

I guess its safe for me to assume that if someone's using my screensharing app, they won't be using Apple's built-in screen sharing feature simultaneously. So SCDynamicStoreCopyConsoleUser should be fine in my case. But it only returns username and uid, in my daemon, how can get security session id of the user from this info?

Came across an example of someone using getutxent api to get list of all interactive sessions: Forum isn't letting me share url. Is this an old api? does it still work fine?

SessionGetInfo

OK.

Is there a way to enumerate all Sessions in daemon and also get their state (i.e. which one is Active)?

No.

how can get security session id of the user from this info?

From the agent running in that session.

Came across an example of someone using getutxent api to get list of all interactive sessions: Forum isn't letting me share url. Is this an old api? does it still work fine?

Three things:

  • DevForums will let you post arbitrary URLs as long as you do it in the clear. See tip 14 in the increasingly misnamed Quinn’s Top Ten DevForums Tips.

  • Yes, this is an old API.

  • Yes, it continues to work as well as it ever did.

However, this is not going to help.

You seems to be in stage 3, bargaining, of the Five Stages of Mac Screen Sharing Developer Grief™, desperately casting around for something that’ll allow you to maintain more of your centralised architecture on the Mac |-: That’s not going to happen. To build a reliable product, you need to follow the daemon-with-zero-or-more-helper-agents architecture.

And I want to stress “reliable”. If you rummage around enough you will find things that look like they’ll help. Many of these are unsupported implementation details. If you build a product based on them, you’ll likely be revisiting this issue in the future.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Five Stages of Mac Screen Sharing Developer Grief

haha. Actually, I've already decided on daemon-with-zero-or-more-helper-agents architecture. The only hiccup now is I wanted the daemon to be able to enumerate sessions as well - just so it has a clear idea of system state. Seems like the only option is to get data from agents and use that in daemon.

Cool.

And don’t forgot to open a DTS code-level support request so we can talk about that bug I mentioned (r. 5636091).

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Session, Desktops and login screen
 
 
Q