David Henningsson <david.hennings...@canonical.com> [2014-09-18 05:57:41 +0200]:
> >
> > It answers the original one, thanks.  Unfortunately (for you :) ) it
> > brings up yet another one (or actually, two related questions).
> 
> When all this is done, you'll write up a nice wiki page or blog post
> explaining it all, right? :-)
> 

To the extent that it can be fully plumbed in all its complicated glory,
yes. :)   A weekend-work in progress, though taking about 5x longer than
originally thought.  But it's coming along. Got a lot done the last couple
of weekends.

> 
> > Background: Here's the first portion of the list you supplied earlier after
> > looking thru the code (which, btw, I greatly appreciate, thanks):
> >
> >   1) Specified server(s) in the call to the pa_context_connect().
> >   2) Specified server(s) in PULSE_SERVER
> >   3) Specified server(s) in X11 property PULSE_SERVER
> >   4) Specified server(s) as in client.conf (the "default-server" key)
> >
> >First question: If the caller does supply NULL as the 'server' parm to
> >pa_context_connect(),
> 
> ...then that means, move on to step 2) in the list above.
> 
>       .
>       .
>       .

Yep, got it now, thanks.  Between your explanation here and building an 
instrumented version from git to dump c->server_list and a few other items,
I think I've finally got a reasonably clear handle on the whole of
pa_context_connect() and friends.

(And I should add that after having wandered thru the code a bit now, I
sympathize with your earlier footnote. :)

Btw, if it's of any use to the project, attached is a heavily commented version
of pa_context_connect(). That's what I'm using in the writeup as the basis
for both a high-level description and a more detailed discussion. (That 
commented version will be included in the writeup pretty much as-is.)

If you or Tanu have time to look thru it and let me know if any of the
commented understanding there is wrong, it would be greatly appreciated.

Thanks again for your time on this.

Glenn
int pa_context_connect(
        pa_context *c,
        const char *server,
        pa_context_flags_t flags,
        const pa_spawn_api *api) {

    int r = -1;

    pa_assert(c);
    pa_assert(PA_REFCNT_VALUE(c) >= 1);

    PA_CHECK_VALIDITY(c, !pa_detect_fork(), PA_ERR_FORKED);
    PA_CHECK_VALIDITY(c, c->state == PA_CONTEXT_UNCONNECTED, PA_ERR_BADSTATE);
    PA_CHECK_VALIDITY(c, !(flags & ~(PA_CONTEXT_NOAUTOSPAWN|PA_CONTEXT_NOFAIL)),
			 PA_ERR_INVALID);
    PA_CHECK_VALIDITY(c, !server || *server, PA_ERR_INVALID);

    //
    // If caller supplied 'server' is NULL, then overwrite it :) with the value 
    // of c->conf->default_server. Despite the implied semantics suggested by its name,
    // c->conf->default_server is not necessarily the value of the 'default-server'
    // key in client.conf, but may be overloaded with other initial state as snapshotted
    // in pa_client_conf_load(). 
    //
    // The precedence order (highest to lowest) appears to be:
    //
    //		- Value obtained from $PULSE_SERVER
    //		- Value obtained from PULSE_SERVER X11 property (if $DISPLAY is set)
    //		- Value obtained from client.conf 'default-server' key
    //		- NULL, if none of the above are set
    //
    // Note that each of the above values is itself permitted to be a general whitespace-
    // delimited list of 'server specification strings' (hereafter "SSS") per:
    //
    //     http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/
    //	        User/ServerStrings
    // 
    // which is a superset of the formats mentioned in the pulseaudio.1 man page.
    // (fdb #84024)
    //
    if (server)
        c->conf->autospawn = false;
    else
        server = c->conf->default_server;

    pa_context_ref(c);

    c->no_fail = !!(flags & PA_CONTEXT_NOFAIL);

    //
    // The semantics of c->server_specified are undocumented in the source, so it is
    // unclear whether the following makes sense relative to whatever those semantics are.
    // In any case, c->server_specified winds up zero only if the overloaded 'server'
    // (as documented above) is NULL.
    //
    c->server_specified = !!server;

    pa_assert(!c->server_list);

    //
    // Here, if 'server' is not NULL, then server_list is set to that value. Per above
    // comments, this may be any of the three state sources mentioned above.
    //
    if (server) {
        if (!(c->server_list = pa_strlist_parse(server))) {
            pa_context_fail(c, PA_ERR_INVALIDSERVER);
            goto finish;
        }

    //
    // This clause is reached only if 'server' is NULL, which in turn can be the case 
    // only if the caller-specified 'server' AND all the other three potential state
    // sources for c->conf->default_server have not been supplied.  When that is the case,
    // c->server_list is still empty, and we go on the following safari to build it.
    //
    } else {
        char *d;

 	//
	// In what follows, c->server_list is constructed so that the topmost item -- i.e.
	// the first one that will be popped off by try_next_connection(c) near the end
	// of this function -- will be the _last_ one prepended in the following clauses
	// using pa_strlist_prepend(). 
	//
	// Thus, the ordering of server_list at the end of this clause will be:
	//
	//	1. "The user instance via PF_LOCAL" [whatever exactly that means]
	//	2. "The system-wide instance via PF_LOCAL"  [ditto]
	//	3. "TCP/IP on the localhost" [conditional on auto_connect_localhost]
	//	4. "Follow the X display" [conditional on auto_connect_display and 
	//	    $DISPLAY having an explicit hostname.]
	//
	
        /* Prepend in reverse order */

	//
	// Conditional on auto_connect_display, and the presence of an explicit hostname
	// in $DISPLAY, prepend that hostname to server_list. Note that this clause does
	// nothing in the case in which the $DISPLAY hostname is implicit, e.g. ":0.0".
	//
        /* Follow the X display */
        if (c->conf->auto_connect_display) {
            if ((d = getenv("DISPLAY"))) {
                d = pa_xstrndup(d, strcspn(d, ":"));

                if (*d)
                    c->server_list = pa_strlist_prepend(c->server_list, d);

                pa_xfree(d);
            }
        }

	//
	// Conditional on auto_connect_localhost, prepend both TCP6 and TCP4 versions of
	// localhost to server_list.
	//
        /* Add TCP/IP on the localhost */
        if (c->conf->auto_connect_localhost) {
            c->server_list = pa_strlist_prepend(c->server_list, "tcp6:[::1]");
            c->server_list = pa_strlist_prepend(c->server_list, "tcp4:127.0.0.1");
        }

	//
	// AFAICT, this prepends the string "XXX/run/pulse/native" to server_list.
        // where XXX is determined during build configure-ation, typically "/var" for
	// Linux-y systems.
	//
        /* The system wide instance via PF_LOCAL */
        c->server_list = pa_strlist_prepend(c->server_list,
			 PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_NATIVE_DEFAULT_UNIX_SOCKET);


	//
	// I gave up trying to trace back thru 3-4 levels of uncommented functions in 
	// order to figure out exactly what this does, but it seems like it prepends 
	// either "native" or "XXXX/native" to server_list, where "XXXX" is some
	// function's idea of the appropriate user runtime dir.
	//
        /* The user instance via PF_LOCAL */
        c->server_list = prepend_per_user(c->server_list);
    }

    //
    // Unclear how this autospawn stuff affects (or doesn't) the operation of the
    // subsequent try_next_connection().
    //
    /* Set up autospawning */
    if (!(flags & PA_CONTEXT_NOAUTOSPAWN) && c->conf->autospawn) {

	#ifdef HAVE_GETUID
        if (getuid() == 0)
            pa_log_debug("Not doing autospawn since we are root.");
        else {
            c->do_autospawn = true;

            if (api)
                c->spawn_api = *api;
        }
	#endif
    }

    //
    // At this point, c->server_list consists of exactly one of the following:
    //
    //	     Explicit SSS 	// If caller-supplied 'server' != NULL
    //
    //	   OR
    //
    //       Default SSS        // If caller-supplied 'server' == NULL but
    //				// c->conf->default_server != NULL
    //
    //	   OR
    //
    //       Items 1-4 from the 'else' clause above
    //
    // Now, via try_next_connection() [which AFAICT, despite its name, really tries *all*
    // entries in c->server_list, not just the "next" one]. It attempts a connect to
    // each server mentioned in server_list, and returns in error only if all the
    // attempts fail.
    //
    pa_log_debug("GDG: server_list: %s", pa_strlist_tostring(c->server_list));
    pa_context_set_state(c, PA_CONTEXT_CONNECTING);
    r = try_next_connection(c);

finish:
    pa_context_unref(c);

    return r;
}
_______________________________________________
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss

Reply via email to