Re: [T(A)ILS-dev] GSoC application: tails-greeter

Delete this message

Reply to this message
Author: M
Date:  
To: tails-dev
Subject: Re: [T(A)ILS-dev] GSoC application: tails-greeter
30.03.2011 21:24, intrigeri пишет:

> At the end of the day you're probably right, but I'd like to see this
> decision based on things a bit more convincing than "is definitely
> harder", see what I mean?
>
> Of course, a custom application would need to rewrite some wheels GDM3
> already has, that is the bits that are common to every display
> manager. On the other hand, I'm for example wondering, among those
> interfaces:
>
> 1. GDM greeter <-> the rest of GDM
> 2. {GDM, other display manager} <-> the rest of the desktop stack
>
> ... which one has the best documented API with planned backward
> compatibility?
>
> Arguments based on other criteria are obviously welcome as well.


As you said: custom app means additional code which re-implements dm parts. More code
means harder maintenance. That's the downside. As for upside - I do not see any :-)

Speaking of interfaces - I see only 2 choice for dm to plug into:
- GDM http://projects.gnome.org/gdm/
- LightDM http://www.freedesktop.org/wiki/Software/LightDM/Design

GDM:
+ default in debian\ubuntu
+ stable code base
+ big established community
- unstable interface for greeter
- big code-base

LightDM:
+ stable and documented interface for greeter
+ small code-base
- lack of community a.t.m.
- only planned for inclusion into ubuntu\debian
https://blueprints.edge.launchpad.net/ubuntu/+spec/packageselection-desktop-n-display-manager

Personally I think that better design of LightDM outweights its disadvantages
considering that .deb packages might be obtained at
https://launchpad.net/~robert-ancell/+archive/lightdm

The major risk involved here is that it's a 1-man project but in worst case I think
greeter might be ported from LightDM to GDM relatively easy (both use GObject and C).

>> I presume it's still very experimental cause it's marked as such :-)
>
> Just curious: reference please? (Neither the Debian package
> description nor the dconf homepage support this assertion.)


my bad: dconf is stable, dconf-tools is experimental.

>> Anyway, it looks like proper way would be to just use gsettings
>> which will abstract backend usage
>
> It seems to me gsettings is not part of Debian Squeeze. Am I wrong?


correct - they are present only in sid.

> I am sorry if our reality-lead requirements tend to prevent you from
> using shinny new tools that are likely to be the way to go in the
> future, but... Tails is currently developped on the basis of Debian
> Squeeze, and will likely be for the next ~18 months. If tails-greeter
> is based on gsettings/dconf, it's unlikely to be of any practical use
> for Tails soon enough.


sad but true :)

> What do you think of this? Does it seem possible to you to implement
> tails-greeter using tools that are usable as of today (Debian Squeeze
> + squeeze-backports) only, in a way that makes it easy later to port
> it to new settings interfaces?


Definitely. This will involve extensive reading of gsettings porting guide and
supplying .convert file to make future transition easier.

> Anyway, again, tails-greeter will not make that much use of a settings
> backend (only default configuration default values, as you stated
> above), so that part shall not draw that much discussion, time and
> energy.


Ok.

> Well, reading this I fear you start designing the whole thing before
> doing anything practical. Of course you need to think and show us a
> big picture at some point (e.g. as part of your GSoC proposal to start
> with, and probably in a more detailed way in the beginning of the
> 3-months coding time), but on the scheduling topic, I think you'd
> better split your project into smaller parts, and design documentation
> would be written before/while implementing every such small part.


Agree.
Btw, "big picture" design probably will me the same as with default greeter of dm
(whatever it is). More fine grained design will be defined by configuration options:
for example we added "option1" which require to re-write preLogin file. Now we're
adding "option2" which needs to send d-bus signal - this leads for the need to
interface dbus from greeter (and corresponding design change). After that - "option3"
which require "blah" action which in turn lead us to need in "blah-blah"
functionality and corresponding design change etc.

> Right, you may have to pressure us a bit so that we make design and UI
> decisions on a few planned features. The more you start doing so in
> advance, the less you'll be stalled because of us during the 3 months
> coding time. In the current state of existing (sometimes slow) Tails
> developers community process, I don't see how this could be
> effectively done during the first week of the coding time.


Fair enough, I should digg into available info on wiki and come with initial proposal
- stay tuned :)

> Bootstrapping this process seems like a very good candidate for the
> "community diving" month, so that:
>
>  * we have enough time to answer your questions
>  * you'll be forced to participate into our development community,
>    we'll be forced to speak to each other, to get to know each other a
>    bit, which is the whole point of the "community diving" month.


Good.

> Some of us have started investigating how Sikuli could be used for
> such matters. See
> https://tails.boum.org/todo/automated_builds_and_tests/ for details.


Very interesting. Although I'm not sure if it will be in place for GSoC timeframe but
it's definitely worth keeping eye on.

> Hrm, well, in my experience using Git as a basic SVN replacement
> (which you are describing) is relatively easy, while getting up to
> speed using its more advanced features (branches, rebasing, history
> rewriting and so on) takes quite more time. My advice to you would be
> not to underestimate this time.


Thanks for the hint.

> I think you'll need to update the design documentation incrementally
> when you change your mind while coding. Else we would have to guess
> from your code what you are trying to do, which makes any review at
> the very least painful. By proceeding like this, the design
> documentation state at the end of the GSoC would give us maintenance
> documentation for free. What do you think?


Agree.

> I did not found what the "abovementioned test setting" refers to,
> which makes it hard for me to understand this part of the schedule.


I was referring to the first "additional option" we're requesting - not usual "login"
and "password" but some TAILS-specific variable.
It looks like I should start some wiki page with project description and glossary
already - just to avoid confusion :)

> Could you please clarify / elaborate what you mean with "iron out
> process"?


What tails-dev do when he suddenly realize that he need to add request for "option-n"
into tails-greeter. I think this process needs to be formalized: the very least is
some table on wiki page to fill, the best - some helper script which asks few
questions and generate stubs, update todos, create bugs for tracking etc.

> A few remarks on week #7:
>
> 1. One week seems *very* short to me to do all this stuff, unless you
>    already are really at ease with code testing and Debian packaging.

>
> 2. I'd be much more comfortable with this schedule if you included (at
>    least unit-) testing as part of your code writing process.

>
> 3. The best way to have Tails developers (and users!) review and test
>    your work would be to have a branch in Tails Git repository where
>    you push your work; depending on how it presents itself, it may
>    deserve a Debian package (in this case, a dedicated repository
>    would be setup for tails-greeter, and a Debian package would be
>    pushed at least at milestone time to the Tails repository) or not
>    (in this case, you'd only work in the Tails Git repository).
>    In either case, you'll need to do some packaging/glue-ing work from
>    the very beginning and all along I think, rather than late in the
>    coding process as you are suggesting.


I partially agree: I also think that code even in working repository supposed to be
compilable and working most of the time so some test are definitely needs to be
written "as-we-go". On the contrary .deb packaging seems like interesting but
secondary task which should be done if time permits.

>> week #8: fix last-minute bugs, do some human-testing if possible.
>
> What do you mean by "human-testing"?


Try to build actual .iso and show to some people who are not tails-dev. Things which
obvious to us might be tricky to people who are less involved with project
development - thus it would be great to have "third-party" opinion. Of course it's
only if everything else is completed in time including .deb packaging.

> Some other things that are missing from your current proposal:
>
> - What programming language do you intend to use?


Right now vala\genie seems like the ideal variant. Python is also an option. Plain
old C - is the "last resort" variant :)

> - The code sample you sent us is in Python. Is this the language you
> are the best at? Could you provide some other code samples?


Attached in simple multi-threaded server I've made ages ago as part of university
programming course. I think I'm mostly proficient in C\C++ and Perl, Python comes
pretty close to Perl.

I can make some test task to prove my skills if you'd like.

> - When will you learn how-to, and setup the needed environment to
> build a Tails system in order to test your work in a realistic
> environment?


This is part of "community bonding" time - essentially a prerequisite to start actual
coding.

I plan to update my application with all the tips and hints from this thread in next
couple of days.

thanks,
Max.
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>

#include "dlog.h"
#include "server.h"

typedef int server_cmd_t;

#define SERVER_CMD_NOP            0
#define SERVER_CMD_QUIT            1
#define SERVER_CMD_FREE_SD        2
#define SERVER_CMD_ADD_SESSION        3
#define SERVER_SESSIONS_MAX        16


server_cmd_t     server_cmd         = SERVER_CMD_NOP;
int         server_cmd_arg;
pthread_mutex_t    server_cmd_mutex    = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t    server_cmd_cv;
pthread_mutex_t server_cmd_send_mutex    = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t     server_cmd_send_cv;
pthread_mutex_t    server_cmd_cv_mutex    = PTHREAD_MUTEX_INITIALIZER;
int         server_exists        = 0;
pthread_mutex_t server_exists_mutex    = PTHREAD_MUTEX_INITIALIZER;


struct server_session_t 
{
    pthread_t thread;
    int socket;
    int sn;
    void (*session_proc) (int);
};


/*______________________________
    server_cmd_send(): 
         send command to server


    IN:
         cmd         command
        cmd_arg        command args

                
    OUT:
         <nothing>
________________________________*/
void
server_cmd_send(server_cmd_t cmd, int cmd_arg)
{
    int old_cancel_state, retval;


    DLOG("<!-- begin of critical section --!>");
    retval = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_cancel_state);
    assert(retval == 0);


    DLOG("lock server_send_cmd mutex (queueing commands)");
    retval = pthread_mutex_lock(&server_cmd_send_mutex);
    assert(retval == 0);


    DLOG("test server existence");
    retval = pthread_mutex_lock(&server_exists_mutex);
    assert(retval == 0);
    if(!server_exists) 
    {
    retval = pthread_mutex_unlock(&server_cmd_send_mutex);
    assert(retval == 0);
    retval = pthread_mutex_unlock(&server_exists_mutex);
    assert(retval == 0);
    return;
    }
    retval = pthread_mutex_unlock(&server_exists_mutex);
    assert(retval == 0);


    DLOG("lock server_cmd mutex");
    retval = pthread_mutex_lock(&server_cmd_mutex);
    assert(retval == 0);


    DLOG("fill server_cmd variables");
    server_cmd = cmd;
    server_cmd_arg = cmd_arg;


    DLOG("lock server_cmd_cv mutex");
    retval = pthread_mutex_lock(&server_cmd_cv_mutex);
    assert(retval == 0);

    
    DLOG("send signal to server and unlock server_cmd mutex");
    retval = pthread_cond_signal(&server_cmd_cv);
    assert(retval == 0);
    retval = pthread_mutex_unlock(&server_cmd_mutex);
    assert(retval == 0);


    DLOG("wait for server");
    retval = pthread_cond_wait(&server_cmd_send_cv, &server_cmd_cv_mutex);
    assert(retval == 0);
    retval = pthread_mutex_unlock(&server_cmd_cv_mutex);
    assert(retval == 0);

    
    DLOG("unlock next server_send_cmd");
    retval = pthread_mutex_unlock(&server_cmd_send_mutex);
    assert(retval == 0);

    
    DLOG("<!-- end of critical section --!>");
    retval = pthread_setcancelstate(old_cancel_state, NULL);
    assert(retval == 0);

        
    DLOG("done.");
}


void *
clients_thread(void *arg)
{
    int retval, server_socket_fd = (int) arg;


    DLOG("enable async cancellation");
    retval = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
    assert(retval == 0);


    DLOG("goto infinity loop");
    while(1) 
    {
    struct sockaddr name;
    socklen_t len;
    int socket_fd;

        
    DLOG("waiting for client...");
    socket_fd = accept(server_socket_fd, &name, &len);


    DLOG("client accepted, send signal to server...");
    server_cmd_send(SERVER_CMD_ADD_SESSION, socket_fd);


    DLOG("done");
    }


/* never would be here */
}

inline void 
server_session_cleanup(void *arg)
{
    DLOG("send SERVER_CMD_FREE_SD");
    server_cmd_send(SERVER_CMD_FREE_SD, (int) arg);
}


void *
server_session(void *arg) 
{
    int retval;
    struct server_session_t *p = (struct server_session_t *) arg;


    DLOG("set cleanup func");
    pthread_cleanup_push(&server_session_cleanup, (void *) p->sn);


    DLOG("set asynchronous cancel type");
    retval = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
    assert(retval == 0);

    
    DLOG("run session");
    p->session_proc(p->socket);

    
    DLOG("run cleanup func");
    pthread_cleanup_pop(1);

    
    DLOG("done.");
    return NULL;
}


void
server_quit()
{
    server_cmd_send(SERVER_CMD_QUIT, 0);
}


int
server(int server_socket_fd, void (*session_proc)(int))
{
    struct server_session_t sessions[SERVER_SESSIONS_MAX];
    int pool[SERVER_SESSIONS_MAX];
    pthread_t clients;
    int t, cmd_arg, retval, sessions_count = 0, server_run = 1, server_quit = 0;
    struct server_session_t * p = NULL;
    server_cmd_t cmd;

    
    DLOG("check server existence");
    retval = pthread_mutex_lock(&server_exists_mutex);    
    assert(retval == 0);


    if(server_exists) 
    {
    DLOG("server already exists");
    retval = pthread_mutex_unlock(&server_exists_mutex);
    assert(retval == 0);
    return 1;
    }


    server_exists = 1;


    retval = pthread_mutex_unlock(&server_exists_mutex);
    assert(retval == 0);

        
    DLOG("init server_cmd_cv");
    retval = pthread_cond_init(&server_cmd_cv, NULL);
    assert(retval == 0);
    retval = pthread_cond_init(&server_cmd_send_cv, NULL);
    assert(retval == 0);


    DLOG("filling pool of session numbers");
    for(t = 0; t < SERVER_SESSIONS_MAX; ++t)
    pool[t] = t;


    DLOG("lock server_cmd mutex");
    retval = pthread_mutex_lock(&server_cmd_mutex);
    assert(retval == 0);


    DLOG("starting thread for accepting clients");
    if(pthread_create(&clients, NULL, &clients_thread, (void *) server_socket_fd)) 
    {
    DLOG("ERR: can't start thread for accepting clients");
    retval = pthread_mutex_unlock(&server_cmd_mutex);
    assert(retval == 0);

        
    return ENOMEM;
    }

    
    DLOG("starting main loop");
    while(server_run) 
    {
    DLOG("waiting for command...");
    retval = pthread_cond_wait(&server_cmd_cv, &server_cmd_mutex);
    assert(retval == 0);

        
    DLOG("save command and it's argument to local variables");
    cmd = server_cmd;
    cmd_arg = server_cmd_arg;


    DLOG("send signal to server_send_cmd that command is recieved");
    retval = pthread_mutex_lock(&server_cmd_cv_mutex);
    assert(retval == 0);
    retval = pthread_cond_signal(&server_cmd_send_cv);
    assert(retval == 0);
    retval = pthread_mutex_unlock(&server_cmd_cv_mutex);
    assert(retval == 0);

        
    DLOG("command recieved");
    switch(cmd) 
    {
        case SERVER_CMD_NOP:
        DLOG("cmd == SERVER_CMD_NOP");
        /* do nothing */
        break;

            
        case SERVER_CMD_ADD_SESSION:
        DLOG("cmd == SERVER_CMD_ADD_SESSION");

                
        if(server_quit)
            break;


        assert(sessions_count <= SERVER_SESSIONS_MAX);
        if(sessions_count == SERVER_SESSIONS_MAX) 
        {
            DLOG("sessions counter == max");    
            break;
        }


        t = pool[sessions_count];
        p = &(sessions[t]);


        DLOG("fill server_session_t fields");
        p->socket = (int) cmd_arg;
        p->sn = t;
        p->session_proc = session_proc;

            
        DLOG("add session");
        if(pthread_create(&(p->thread), NULL, &server_session, (void *) p)) 
        {
            DLOG("session add failed");
        } 
        else 
        {
            DLOG("session added");
            ++sessions_count;
        }
        break;

            
        case SERVER_CMD_FREE_SD: 
        DLOG("cmd == SERVER_CMD_FREE_SD");

            
        assert(sessions_count);
        t = cmd_arg;
        assert(t >=0 && t < SERVER_SESSIONS_MAX);

                
        DLOG("join session");
        retval = pthread_join(sessions[pool[t]].thread, NULL);
        assert(retval == 0);


        DLOG("close socket");
        close(sessions[pool[t]].socket);


        DLOG("free session's pool entry");
        --sessions_count;
        pool[sessions_count] = t;


        if(sessions_count == 0 && server_quit)
            server_run = 0;
        break;

            
        case SERVER_CMD_QUIT:
        DLOG("cmd == SERVER_CMD_QUIT");

                
        if(server_quit)
            break;


        DLOG("cancel accepting thread"); 
        retval = pthread_cancel(clients);
        assert(retval == 0);


        DLOG("cancel sessions");
        for(t = 0; t < sessions_count; ++t)
        {
            retval = pthread_cancel(sessions[pool[t]].thread);
            assert(retval == 0);
        }


        server_quit = 1;
        break;
    }
    }


    DLOG("unlock server_cmd mutex");
    retval = pthread_mutex_unlock(&server_cmd_mutex);
    assert(retval == 0);


    DLOG("join clients' accepting thread");
    retval = pthread_join(clients, NULL);
    assert(retval == 0);


    DLOG("server_exists -> 0");
    retval = pthread_mutex_lock(&server_exists_mutex);
    assert(retval == 0);


    server_exists = 0;


    retval = pthread_mutex_unlock(&server_exists_mutex);
    assert(retval == 0);


    DLOG("done.");
    return 0;
}

#ifndef __SERVER_H__
#define __SERVER_H__

int server(int, void (*session_proc)(int));
void server_quit();

#endif