Pages

Showing posts with label linux. Show all posts
Showing posts with label linux. Show all posts

Tuesday, March 17, 2015

Lispvirt Announcements

Hi everybody!

I'd like to announce the first release (1.0.0) of Lispvirt: Common Lisp bindings for Libvirt.

You can download the source code from GitHub at:
https://github.com/jcfaracco/lispvirt

There you can find any instrunctions to install, test and use Lispvirt. There are some examples and tests to teach how to use the API. As this release is the first one, there is nothing to announce as a new feature.

This version is based on Libvirt (>= 1.2). So, if you are using an old version of libvirt, I cannot guarantee that Lispvirt will work.

Please, clone the code and enjoy!

If you want to contribute, please, send me a message, email, commits or anything you want.
We still need many tests to check if the API is correctly implemented, examples and some missing structures.

Any contribution is welcome and I would be glad if people help me. =D

Monday, December 17, 2012

SQLite3: An Overview with Code Snippets in C and Python.

SQLite Introduction

SQLite is a light Database library written in C. You can use this library for database operations instead of using a DBMS process running. If your application needs a database but the operations of it are so simple and the access flow is not so high, it's recommended that you use this database approach. The current version of SQLite is 3.x.

In this post we will introduce some basics functions of the library and they will be showed in two programming language: C and Python. The intention is show you how easily the SQLite is.

The database schema used is the example below.





This is a Company schema and it has your employees, the dependets of the employees and some management of Company's projects.

We going to create the Database tables and do some basic operations. First, we create the tables, your primary keys and foreing keys. You can create a function to do it.

Here we create a function called

C:
int create_database_tables(sqlite3 *handle)

In Python, the constructor __init__ of the class create the tables

Obviously, don't forget to include the libraries

C:
#include <sqlite3.h>

Python:
import sqlite3

You have to pass the sqlite3 handle as an argument to execute the statements. Each SQL statemens was defined in string constant variables. After the statements will be executed. The body of the function can be viewed below.

C:
int create_database_tables(sqlite3 *handle) {
 
    /** ERROR message. **/   
    char *errmsg;

    /** String who create the table 'employee'. **/     
    const char * employee = "CREATE TABLE IF NOT EXISTS employee \
                             (cpf_emp TEXT PRIMARY KEY, \
                             name TEXT NOT NULL, code TEXT, \
dpt_acro TEXT, begin_date TEXT, \
FOREIGN KEY(code) REFERENCES employee(cpf_emp), \
FOREIGN KEY(dpt_acro) \
                             REFERENCES departament(acro_dept))";

    /** String who create the table 'relative'. **/
    const char * relative = "CREATE TABLE IF NOT EXISTS relative \
                             (cpf_emp TEXT, name TEXT, \
                             gender TEXT, PRIMARY KEY (cpf_emp, name), \
                             FOREIGN KEY(cpf_emp) \
                             REFERENCES employee(cpf_emp))";
 
    /** String who create the table 'department'. **/   
    const char * department = "CREATE TABLE IF NOT EXISTS department \
                               (acro_dpt TEXT PRIMARY KEY, \
                               name_dpt TEXT NOT NULL, \
                               cpf_emp TEXT NOT NULL, \
                               FOREIGN KEY(cpf_emp) \
                               REFERENCES employee(cpf_emp))";

    /** String who create the table 'project'. **/   
    const char * project = "CREATE TABLE IF NOT EXISTS project \
                            (number_project INTEGER PRIMARY KEY, \
                            name_project TEXT NOT NULL)";
 
    /** String who create the table 'control'. **/   
    const char * control = "CREATE TABLE IF NOT EXISTS control \
                            (acro_dpt TEXT, number_project TEXT, \
                            PRIMARY KEY(acro_dpt, number_project), \
                            FOREIGN KEY(acro_dpt) \
                            REFERENCES department(acro_dept), \
                            FOREIGN KEY(number_project) \
                            REFERENCES project(number_project))";
 
    /** String who create the table 'develop'. **/   
    const char * develop = "CREATE TABLE IF NOT EXISTS develop \
                            (cpf_emp TEXT, number_project TEXT, \
                            time_worked FLOAT, \
                            PRIMARY KEY(cpf_emp, number_project), \
                            FOREIGN KEY(cpf_emp) \
                            REFERENCES employee(cpf_emp), \
                            FOREIGN KEY(number_project) \
                            REFERENCES project(number_project))";
 
    /** Execute all the SQL create tables statements. **/   
    if(sqlite3_exec(handle,employee,0,0,&errmsg) != SQLITE_OK) {
        printf("ERROR: %s\n", errmsg);
        return -1;
    }
     
    if(sqlite3_exec(handle,relative,0,0,&errmsg) != SQLITE_OK) {
        printf("ERROR: %s\n", errmsg);
        return -1;
    }
 
    if(sqlite3_exec(handle,department,0,0,&errmsg) != SQLITE_OK) {
        printf("ERROR: %s\n", errmsg);
        return -1;
    }
 
    if(sqlite3_exec(handle,project,0,0,&errmsg) != SQLITE_OK) {
        printf("ERROR: %s\n", errmsg);
        return -1;
    }
     
    if(sqlite3_exec(handle,control,0,0,&errmsg) != SQLITE_OK) {
        printf("ERROR: %s\n", errmsg);
        return -1;
    }
     
    if(sqlite3_exec(handle,develop,0,0,&errmsg) != SQLITE_OK) {
        printf("ERROR: %s\n", errmsg);
        return -1;
    }
     
    return 1;
}


Python:
class Database:
 
    def __init__(self, cur):

        # String who create the table 'employee'.
        employee = 'CREATE TABLE IF NOT EXISTS employee \
                    (cpf_emp TEXT PRIMARY KEY, \
                    name TEXT NOT NULL, code TEXT, \
dpt_acro TEXT, begin_date TEXT, \
FOREIGN KEY(code) REFERENCES employee(cpf_emp), \
FOREIGN KEY(dpt_acro) \
                    REFERENCES departament(acro_dept))'

        # String who create the table 'relative'.
        relative = 'CREATE TABLE IF NOT EXISTS relative \
                    (cpf_emp TEXT, name TEXT, \
                    gender TEXT, PRIMARY KEY (cpf_emp, name), \
                    FOREIGN KEY(cpf_emp) \
                    REFERENCES employee(cpf_emp))'

        # String who create the table 'department'.
        department = 'CREATE TABLE IF NOT EXISTS department \
                      (acro_dpt TEXT PRIMARY KEY, \
                      name_dpt TEXT NOT NULL, \
                      cpf_emp TEXT NOT NULL, \
                      FOREIGN KEY(cpf_emp) \
                      REFERENCES employee(cpf_emp))'

        # String who create the table 'project'.
        project = 'CREATE TABLE IF NOT EXISTS project \
                   (number_project INTEGER PRIMARY KEY, \
                   name_project TEXT NOT NULL)'
   
        # String who create the table 'control'.
        control = 'CREATE TABLE IF NOT EXISTS control \
                   (acro_dpt TEXT, number_project TEXT, \
                   PRIMARY KEY(acro_dpt, number_project), \
                   FOREIGN KEY(acro_dpt) \
                   REFERENCES department(acro_dept), \
                   FOREIGN KEY(number_project) \
                   REFERENCES project(number_project))'
   
        # String who create the table 'develop'.
        develop = 'CREATE TABLE IF NOT EXISTS develop \
                   (cpf_emp TEXT, number_project TEXT, \
                   time_worked FLOAT, \
                   PRIMARY KEY(cpf_emp, number_project), \
                   FOREIGN KEY(cpf_emp) \
                   REFERENCES employee(cpf_emp), \
                   FOREIGN KEY(number_project) \
                   REFERENCES project(number_project))'
     
        try:          
            # Execute all the SQL create tables statements.
            cur.execute(employee)
            cur.execute(relative)
            cur.execute(departament)
            cur.execute(project)
            cur.execute(control)
            cur.execute(develop)
         
        except sqlite3.Error, e:
            # Throw an error if it occurs.
            print 'ERROR: ' % e.args[0]


The char pointer errmsg in C shows an error string if the SQL statement doesn't execute properly. However, in Python, we use try and except to catch an error of the SQL statement execution.

Before populate the tables, let's going to see the main() fucntion. There, we can verify how the database was initiated. The code is so simple.

C:
int main(int argc, char *args[]) {
 
    sqlite3_stmt * stmt;
 
    sqlite3 * handle;
 
    /** Open connection with the database. **/
    /** If the file does not exists, create it. **/
    if (sqlite3_open("sample.db",&handle)) {
      printf("Connection falied!\n");
      return -1;
    }

    /** Create the tables of our database. **/
    create_database_tables(handle);

    /** Populate the tables with data. **/ 
    insert_data_to_database(handle);
 
    /** Execute two queries: **/
    /**    A normal SELECT and a SELECT with INNER JOIN. **/
    select_data_of_database(handle, stmt);
    select_inner_join_data_of_database(handle, stmt);
 
    /** Create a Trigger that verifies alterations **/
    /** at the name column of the project's table. **/
    log_trigger_database(handle, stmt);
 
    /** Close connection. **/
    sqlite3_close(handle);
 
    return 0;
}

Python:
if __name__ == "__main__":
 
    con = None
 
    try:

        # Open connection with the database.
        # If the file does not exists, create it.

        con = sqlite3.connect('sample.db')

        # Get the cursor of connection.   
        cur = con.cursor()
     
        # Init the database with the constructor.
        db = Database(cur)
     
        con.commit()

        # Insert data into the tables.
        db.insert_data_into_database(cur)

        # Commit the modifications.   
        con.commit()

        # Execute the samples of SELECT.   
        db.select_data_of_database(cur)   
        db.select_inner_join_data_of_database(cur)
     
        # Create the Trigger example.
        db.log_trigger_database(cur)
     
    except sqlite3.Error, e:
        # Throw an error if it occurs.
        print "Error %s:" % e.args[0]
     
        sys.exit(1)
     
    finally:
        # Close the connection
        if con:
            con.close()


You can easily see the functions being called. In main() we simply execute the sample functions. In Python, if we don't commit the modifications, the next functions doesn't work. If we create the tables and, after, insert the data, the SELECT doesn't return values.

The next step is populate the tables with data. You can easily do it executing the strings with INSERT statements. The code seems like the create tables function. But, instead of strings with "CREATE TABLE..." we can use "INSERT..." and any other SQL word like "COMMIT", "ROLLBACK", "SELECT...", etc.

C:
int insert_data_into_database(sqlite3 *handle) {

    /** ERROR message. **/    
    char *errmsg;

    /** Insert data into 'employee'. **/    
    sqlite3_exec(handle,"INSERT INTO employee VALUES \
                 ('123456789','John Smith','333445555','5','09-JAN-55')",
                  0,0,&errmsg);
    sqlite3_exec(handle,"INSERT INTO employee VALUES \
                 ('333445555','Franklin Wong','888665555','5','08-DEZ-45')",
                  0,0,&errmsg);
    sqlite3_exec(handle,"INSERT INTO employee VALUES \
                 ('999887777','Alicia Zelaya','987654321','4','19-JUL-58')",
                  0,0,&errmsg);
    sqlite3_exec(handle,"INSERT INTO employee VALUES \
                 ('987654321','Jeniffer Wallace','888665555','4','20-JUN-31')",
                  0,0,&errmsg);
    sqlite3_exec(handle,"INSERT INTO employee VALUES \
                 ('666884444','Ramesh Narayan','333445555','5','15-SET-52')",
                  0,0,&errmsg);
    sqlite3_exec(handle,"INSERT INTO employee VALUES \
                 ('453453453','Joyce English','333445555','5','31-JUL-62')",
                  0,0,&errmsg);
    sqlite3_exec(handle,"INSERT INTO employee VALUES \
                 ('987987987','Ahmad Jabbar','987654321','4','29-MAR-59')",
                  0,0,&errmsg);
    sqlite3_exec(handle,"INSERT INTO employee VALUES \
                 ('888665555','James Borg',null,'1','10-NOV-27')",
                  0,0,&errmsg);

    /** Insert data into 'relative'. **/     
    sqlite3_exec(handle,"INSERT INTO relative VALUES ('333445555','Alice','F')",
                 0,0,&errmsg);
    sqlite3_exec(handle,"INSERT INTO relative VALUES ('333445555','Theodore','M')",
                 0,0,&errmsg);
    sqlite3_exec(handle,"INSERT INTO relative VALUES ('333445555','Joy','F')",
                 0,0,&errmsg);
    sqlite3_exec(handle,"INSERT INTO relative VALUES ('987654321','Abner','M')",
                 0,0,&errmsg);
    sqlite3_exec(handle,"INSERT INTO relative VALUES ('123456789','Michael','M')",
                 0,0,&errmsg);
    sqlite3_exec(handle,"INSERT INTO relative VALUES ('123456789','Alice','F')",
                 0,0,&errmsg);
    sqlite3_exec(handle,"INSERT INTO relative VALUES ('123456789','Elizabeth','F')",
                 0,0,&errmsg);

    /** Insert data into 'department'. **/
    sqlite3_exec(handle,"INSERT INTO departament VALUES \
                 ('res','Research','333445555')",0,0,&errmsg);
    sqlite3_exec(handle,"INSERT INTO departament VALUES  \
                 ('adm','Administration','987654321')",0,0,&errmsg);
    sqlite3_exec(handle,"INSERT INTO departament VALUES  \
                 ('man','Management','888665555')",0,0,&errmsg);

    /** Insert data into 'project'. **/     
    sqlite3_exec(handle,"INSERT INTO project VALUES ('1','Product X')",
                 0,0,&errmsg);
    sqlite3_exec(handle,"INSERT INTO project VALUES ('2','Product Y')",
                 0,0,&errmsg);
    sqlite3_exec(handle,"INSERT INTO project VALUES ('3','Product Z')",
                 0,0,&errmsg);
    sqlite3_exec(handle,"INSERT INTO project VALUES ('10','Automation')",
                 0,0,&errmsg);
    sqlite3_exec(handle,"INSERT INTO project VALUES ('20','Reorganization')",
                 0,0,&errmsg);
    sqlite3_exec(handle,"INSERT INTO project VALUES ('30','Optimization')",
                 0,0,&errmsg);

    /** Insert data into 'control'. **/      
    sqlite3_exec(handle,"INSERT INTO control VALUES ('res','Product X')",
                 0,0,&errmsg);
    sqlite3_exec(handle,"INSERT INTO control VALUES ('res','Product Y')",
                 0,0,&errmsg);
    sqlite3_exec(handle,"INSERT INTO control VALUES ('res','Product Z')",
                 0,0,&errmsg);
    sqlite3_exec(handle,"INSERT INTO control VALUES ('man','Automation')",
                 0,0,&errmsg);
    sqlite3_exec(handle,"INSERT INTO control VALUES ('adm','Reorganization')",
                 0,0,&errmsg);
    sqlite3_exec(handle,"INSERT INTO control VALUES ('adm','Optimization')",
                 0,0,&errmsg);
 
    /** Insert data into 'develop'. **/
    sqlite3_exec(handle,"INSERT INTO develop VALUES ('123456789','1',32.5)",
                 0,0,&errmsg);
    sqlite3_exec(handle,"INSERT INTO develop VALUES ('123456789','2',7.5)",
                 0,0,&errmsg);
    sqlite3_exec(handle,"INSERT INTO develop VALUES ('666884444','3',40.0)",
                 0,0,&errmsg);
    sqlite3_exec(handle,"INSERT INTO develop VALUES ('453453453','1',20.0)",
                 0,0,&errmsg);
    sqlite3_exec(handle,"INSERT INTO develop VALUES ('453453453','2',20.0)",
                 0,0,&errmsg);
    sqlite3_exec(handle,"INSERT INTO develop VALUES ('333445555','2',10.0)",
                 0,0,&errmsg);
    sqlite3_exec(handle,"INSERT INTO develop VALUES ('333445555','3',10.0)",
                 0,0,&errmsg);
    sqlite3_exec(handle,"INSERT INTO develop VALUES ('333445555','10',10.0)",
                 0,0,&errmsg);
    sqlite3_exec(handle,"INSERT INTO develop VALUES ('333445555','20',10.0)",
                 0,0,&errmsg);
    sqlite3_exec(handle,"INSERT INTO develop VALUES ('999887777','30',30.0)",
                 0,0,&errmsg);
    sqlite3_exec(handle,"INSERT INTO develop VALUES ('999887777','10',10.0)",
                 0,0,&errmsg);
    sqlite3_exec(handle,"INSERT INTO develop VALUES ('987987987','10',35.0)",
                 0,0,&errmsg);
}

Python:
    def insert_data_into_database(self, cur):
     
        try:          
            # Insert data into 'employee'.
            cur.execute("INSERT INTO employee VALUES \
                     ('123456789','John Smith','333445555','5','09-JAN-55')")
            cur.execute("INSERT INTO employee VALUES \
                     ('333445555','Franklin Wong','888665555','5','08-DEZ-45')")
            cur.execute("INSERT INTO employee VALUES \
                     ('999887777','Alicia Zelaya','987654321','4','19-JUL-58')")
            cur.execute("INSERT INTO employee VALUES \
                     ('987654321','Jeniffer Wallace','888665555','4','20-JUN-31')")
            cur.execute("INSERT INTO employee VALUES \
                     ('666884444','Ramesh Narayan','333445555','5','15-SET-52')")
            cur.execute("INSERT INTO employee VALUES \
                     ('453453453','Joyce English','333445555','5','31-JUL-62')")
            cur.execute("INSERT INTO employee VALUES \
                     ('987987987','Ahmad Jabbar','987654321','4','29-MAR-59')")
            cur.execute("INSERT INTO employee VALUES \
                     ('888665555','James Borg',null,'1','10-NOV-27')")

            # Insert data into 'relative'.  
            cur.execute("INSERT INTO relative VALUES \
                        ('333445555','Alice','F')")
            cur.execute("INSERT INTO relative VALUES \
                        ('333445555','Theodore','M')")
            cur.execute("INSERT INTO relative VALUES \
                        ('333445555','Joy','F')")
            cur.execute("INSERT INTO relative VALUES \
                        ('987654321','Abner','M')")
            cur.execute("INSERT INTO relative VALUES \
                        ('123456789','Michael','M')")
            cur.execute("INSERT INTO relative VALUES \
                        ('123456789','Alice','F')")
            cur.execute("INSERT INTO relative VALUES \
                        ('123456789','Elizabeth','F')")

            # Insert data into 'department'.
            cur.execute("INSERT INTO departament VALUES \
                        ('res','Research','333445555')")
            cur.execute("INSERT INTO departament VALUES \
                        ('adm','Administration','987654321')")
            cur.execute("INSERT INTO departament VALUES \
                        ('man','Management','888665555')")

            # Insert data into 'project'.   
            cur.execute("INSERT INTO project VALUES ('1','Product X')")
            cur.execute("INSERT INTO project VALUES ('2','Product Y')")
            cur.execute("INSERT INTO project VALUES ('3','Product Z')")
            cur.execute("INSERT INTO project VALUES ('10','Automation')")
            cur.execute("INSERT INTO project VALUES ('20','Reorganization')")
            cur.execute("INSERT INTO project VALUES ('30','Optimization')")
 
            # Insert data into 'control'.
            cur.execute("INSERT INTO control VALUES ('res','Product X')")
            cur.execute("INSERT INTO control VALUES ('res','Product Y')")
            cur.execute("INSERT INTO control VALUES ('res','Product Z')")
            cur.execute("INSERT INTO control VALUES ('man','Automation')")
            cur.execute("INSERT INTO control VALUES ('adm','Reorganization')")
            cur.execute("INSERT INTO control VALUES ('adm','Optimization')")

            # Insert data into 'develop'.   
            cur.execute("INSERT INTO develop VALUES ('123456789','1',32.5)")
            cur.execute("INSERT INTO develop VALUES ('123456789','2',7.5)")
            cur.execute("INSERT INTO develop VALUES ('666884444','3',40.0)")
            cur.execute("INSERT INTO develop VALUES ('453453453','1',20.0)")
            cur.execute("INSERT INTO develop VALUES ('453453453','2',20.0)")
            cur.execute("INSERT INTO develop VALUES ('333445555','2',10.0)")
            cur.execute("INSERT INTO develop VALUES ('333445555','3',10.0)")
            cur.execute("INSERT INTO develop VALUES ('333445555','10',10.0)")
            cur.execute("INSERT INTO develop VALUES ('333445555','20',10.0)")
            cur.execute("INSERT INTO develop VALUES ('999887777','30',30.0)")
            cur.execute("INSERT INTO develop VALUES ('999887777','10',10.0)")
            cur.execute("INSERT INTO develop VALUES ('987987987','10',35.0)")
         
        except sqlite3.Error, e:
            # Throw an error if it occurs.
            print 'ERROR: ' % e.args[0]

Now, let's see two examples of SELECT statement.

The first function contains the simple SELECT.

C:
int select_data_of_database(sqlite3 * handle, sqlite3_stmt * stmt) {

    const char * tail;

    /** String with SELECT statement. **/
    const char * select_emp = "SELECT * FROM employee";
 
    /** Execute the SQL statement. **/
    if(sqlite3_prepare_v2(handle,select_emp,-1,&stmt,&tail) != SQLITE_OK) {
        printf("ERROR: Select wasn't executed!");
      return -1;
    }

    /** Output of the execution. **/
    printf("%s\n", select_emp);
    printf("==========================================================\n");
    while (sqlite3_step(stmt) == SQLITE_ROW) {
        printf("%s | ", sqlite3_column_text(stmt, 0));
        printf("%s | ", sqlite3_column_text(stmt, 1));
        printf("%s | ", sqlite3_column_text(stmt, 2));
        printf("%s | ", sqlite3_column_text(stmt, 3));
        printf("%s\n", sqlite3_column_text(stmt, 4));
    }
    printf("==========================================================\n\n");

    /** Finish the statement. **/  
    sqlite3_finalize(stmt);
 
    return 1;
}

Python:
    def select_data_of_database(self, cur):
     
        try:
            # Execute the SQL statement.
            cur.execute("SELECT * FROM employee")

            # Output of the execution.
            print "SELECT * FROM employee"
            print "=========================================================="
            for row in cur:
                print row[0], '|', row[1], '|', row[2], '|', row[3], '|', row[4]
            print "==========================================================\n"
     
        except sqlite3.Error, e:
            print 'ERROR: ' % e.args[0]


The sqlite3_prepare_v2() function prepare the output of the SQL execution to be accessed with a pointer. On the other hand, the output data can accessed in sequence with the sqlite3_step() function. The columns can be accessed with the sqlite3_column_text() function, passing the statement pointer and the index of the column.

The other example function shows a INNER JOIN statement.

C:
int select_inner_join_data_of_database(sqlite3 * handle, sqlite3_stmt * stmt) {

    const char * tail;

    /** String with SELECT statement. **/
    const char * select_emp_and_rel = "SELECT * FROM employee \
                                       AS C JOIN relative \
                                       AS R ON C.cpf_emp=R.cpf_emp;";

    /** Execute the SQL statement. **/ 
    if(sqlite3_prepare_v2(handle,select_emp_and_rel,-1,&stmt,&tail) != SQLITE_OK) {
        printf("ERROR: Select wasn't executed!");
      return -1;
    }
 
    /** Output of the execution. **/
    printf("%s\n", select_emp_and_rel);
    printf("==========================================================\n");
    while (sqlite3_step(stmt) == SQLITE_ROW) {
        printf("%s | ", sqlite3_column_text(stmt, 0));
        printf("%s | ", sqlite3_column_text(stmt, 1));
        printf("%s | ", sqlite3_column_text(stmt, 2));
        printf("%s | ", sqlite3_column_text(stmt, 3));
        printf("%s | ", sqlite3_column_text(stmt, 4));
        printf("%s | ", sqlite3_column_text(stmt, 5));
        printf("%s | ", sqlite3_column_text(stmt, 6));
        printf("%s\n", sqlite3_column_text(stmt, 7));
    }
    printf("==========================================================\n\n");

    /** Finish the statement. **/   
    sqlite3_finalize(stmt);
 
    return 1;
}

Python:
    def select_inner_join_data_of_database(self, cur):
     
        try:
            # Execute the SQL statement.
            cur.execute("SELECT * FROM employee AS C JOIN \
                         relative AS R ON C.cpf_emp=R.cpf_emp")
         
            # Output of the execution.
            print "SELECT * FROM employee AS C JOIN \
                   relative AS R ON C.cpf_emp=R.cpf_emp"
            print "=========================================================="
            for row in cur:
                print row[0], '|', row[1], '|', row[2], '|', row[3], '|',
                      row[4], '|', row[5], '|', row[6], '|', row[7]
            print "==========================================================\n"
     
        except sqlite3.Error, e:
            # Throw an error if it occurs.
            print 'ERROR: ' % e.args[0]


The process is exactly the same, but observe the difference between strings.

Finally, the sample has a function who creates a trigger. The trigger records changes in project's names in a table called "log"and, when it happens, the time and date is recorded too.

C:
int log_trigger_database(sqlite3 * handle, sqlite3_stmt * stmt) {

    /** String who create the 'log' table used by trigger. **/
    const char * log = "CREATE TABLE log(id integer PRIMARY KEY, \
                        project_old_name TEXT, \
                        project_new_name TEXT, date TEXT)";

    /** String who create the trigger. **/
    const char * trigger = "CREATE TRIGGER project_trigger \
                            UPDATE OF name_project ON project \
                            BEGIN INSERT INTO Log(project_old_name, \
                            project_new_name, date) \
                            VALUES(old.name_project, new.name_project, \
                            datetime('now')); END;";

    /** UPDATE statement to verify the trigger. **/
    const char * update_trigger = "UPDATE project SET name_project='Product W' \
                                   WHERE number_project='3'";

    /** String with SELECT statement. **/
    const char * select_log = "SELECT * FROM log";
 
    const char * tail;
 
    char *errmsg;
 
    /** Execute all the SQL statement. **/ 
    if(sqlite3_exec(handle,log,0,0,&errmsg) != SQLITE_OK) {
     printf("ERROR: %s\n", errmsg);
      return -1;
    }
 
    if(sqlite3_exec(handle,trigger,0,0,&errmsg) != SQLITE_OK) {
     printf("ERROR: %s\n", errmsg);
      return -1;
    }
 
    if(sqlite3_exec(handle,update_trigger,0,0,&errmsg) != SQLITE_OK) {
     printf("ERROR: %s\n", errmsg);
      return -1;
    }
 
    if(sqlite3_prepare_v2(handle,select_log,-1,&stmt,&tail) != SQLITE_OK) {
        printf("ERROR: Select wasn't executed!");
      return -1;
    }
 
    /** Output of the execution. **/
    printf("%s\n", select_log);
    printf("==========================================================\n");
    while (sqlite3_step(stmt) == SQLITE_ROW) {
        printf("%d | ", sqlite3_column_int(stmt, 0));
        printf("%s | ", sqlite3_column_text(stmt, 1));
        printf("%s | ", sqlite3_column_text(stmt, 2));
        printf("%s\n", sqlite3_column_text(stmt, 3));
    }
    printf("==========================================================\n\n");
 
    /** Finish the statement. **/
    sqlite3_finalize(stmt);

}

Python:
    def log_trigger_database(self, cur):

        try:
            # Create the table 'log' used by the trigger.
            cur.execute("CREATE TABLE log(id integer PRIMARY KEY, \
                         project_old_name TEXT, project_new_name TEXT, \
                         date TEXT)")

            # Create the trigger.
            cur.execute("CREATE TRIGGER project_trigger \
                         UPDATE OF name_project ON project \
                         BEGIN \
                         INSERT INTO Log(project_old_name, project_new_name, date) \
                         VALUES(old.name_project, new.name_project, \
                         datetime('now')); \
                         END;")

            # Execute the UPDATE to check the trigger.
            cur.execute("UPDATE project SET name_project='Product W' \
                         WHERE number_project='3'")

            # Check if trigger works.
            cur.execute("SELECT * FROM log")
         
            # Output of the execution.
            print "SELECT * FROM log"
            print "=========================================================="
            for row in cur:
                print row[0], '|', row[1], '|', row[2], '|', row[3]
            print "==========================================================\n"
     
        except sqlite3.Error, e:
            # Throw an error if it occurs.
            print 'ERROR: ' % e.args[0]


As you can see, this function create the table 'log', create the trigger, update a specific project's name and execute a SELECT statement in the table 'log'. The output of SELECT statement is printed in the output stream.

Conclusion

If you don't want to use a DBMS, SQLite is a good alternative for small databases. Remember that SQLite is only a library who manipulates a file. So, a large amount of data can cause latency when you recover a data.

In Python, we have some differences from C and they will show in a next post.

Observation: The database figure will be changed for a better figure.

Sunday, February 5, 2012

Pyjamas: Um Web Toolkit prático e simples para o desenvolvimento de aplicações AJAX

Nesse novo post, gostaria de demonstrar um pouco sobre uma ferramenta que caiu como uma luva em um dos projetos que iniciei a pouco tempo: o framework Pyjamas. O Pyjamas se auto define como um compilador Python-to-JavaScritp. Isso significa, na verdade, que ele é capaz de gerar um script em JavaScript a partir de códigos feitos em Python.

Inclusive, as bibliotecas do Pyjamas incorporam um Web Toolkit capaz de gerar Web Widgets. Em outras palavras, com o Pyjamas você pode criar sites com visuais no estilo Desktop, por exemplo. Sites com menus dinâmicos, botões, barras, diálogos, etc. Por esse motivo, pode se dizer que o Pyjamas é uma ferramenta quase alternativa ao Google Web Toolkit (GWT). Só não é por completo pelo fato daquela utilizar chamadas das bibliotecas do GWT para suprir algumas deficiências que ela ainda possui.

No nosso caso, construir todo um sistema Web fazendo uso de AJAX, HTML e CSS seria muito custoso e levaria a muito tempo de desenvolvimento. Com esse Web Toolkit, nos preocupamos somente com a disposição dos elementos do site e com o estilo dele (CSS). Sobrando um enorme tempo para nos dedicar a funcionalidade e operacionalidade do sistema.


Exemplo do uso

Irei, a seguir, somente mostrar um breve exemplo do uso do Pyjamas, uma vez que você encontra muito material com qualidade no site da ferramenta.

Para inicio, você precisa obviamente da própria ferramenta. Há três modos de obtê-la: via apt-get (em sistemas Debian-like), fazendo o Download via link ou baixando o repositório git pelo,

git clone git://pyjs.org/git/pyjamas.git

Eu recomendaria o Download tanto do código-fonte quanto do repositótio git. A versão da ferramenta via apt-get é mais antiga e dá mais problemas na construção do que as citadas anteriormente.

Siga toda a instrução de compilação e construção do Pyjamas descrita nos arquivos do framework e pronto. Após construído, note que fora criado a pasta 'bin'. Todas as ferramentas provenientes do Pyjamas se encontram neste diretório:
  • pyjsbuild: compila o código em Python e constrói toda a sua página web com JavaScript;
  • pyjd: um Depurador (Debugger);
  • pyjscompile: o próprio compilador sem construção de output;
  • pyjampiler: também é (alternativo) um compilador um pouco menor que o pyjscompile;
Podemos construir cada classe em um arquivo separado fazendo as importações necessárias, porém iremos montar o script em um único arquivo para facilitar o exemplo.

Para iniciarmos nosso script, importamos as bibliotecas que serão utilizadas,

from pyjamas.ui.RootPanel import RootPanel
from pyjamas.ui.SimplePanel import SimplePanel
from pyjamas.ui.HorizontalPanel import HorizontalPanel
from pyjamas.ui import HasAlignment
from pyjamas.ui.Image import Image
from pyjamas.ui.Button import Button
from pyjamas.ui.ToggleButton import ToggleButton
from pyjamas.ui.MenuBar import MenuBar
from pyjamas.ui.MenuItem import MenuItem
from pyjamas import Window

Nesse exemplo, iremos mostrar a criação de uma Barra de Menus e dois botões, um normal e um toggle.

Começaremos pela classe que irá representar a Barra de Menus.

class MenubarExample(SimplePanel):
    def __init__(self):
        SimplePanel.__init__(self)
     
        menu1 = MenuBar(vertical=True)
        menu1.addItem("Open", getattr(self, "onMenuItemOpen"))
        menu1.addItem("Save As...", getattr(self, "onMenuItemSaveAs"))

        menu2 = MenuBar(vertical=True)
        menu2.addItem("Undo", getattr(self, "onMenuItemUndo"))
        menu2.addItem("Redo", getattr(self, "onMenuItemRedo"))
             
        menu3 = MenuBar(vertical=True)
        menu3.addItem("Help", getattr(self, "onMenuItemHelp"))
        menu3.addItem("About", getattr(self, "onMenuItemAbout"))

        menubar = MenuBar(vertical=False)
        menubar.addItem(MenuItem("File", menu1))
        menubar.addItem(MenuItem("Edit", True, menu2))
        menubar.addItem(MenuItem("About", True, menu3))
     
        self.add(menubar)

    def onMenuItemOpen(self):
        Window.alert("Item Open selected")

    def onMenuItemSaveAs(self):
        Window.alert("Item Save As selected")

    def onMenuItemUndo(self):
        Window.alert("Item Undo selected")

    def onMenuItemRedo(self):
        Window.alert("Item Redo selected")
    
    def onMenuItemHelp(self):
        Window.alert("Item Help selected")
 
    def onMenuItemAbout(self):
        Window.alert("Item About selected")

E a classe que corresponde a barra com os dois botões é apresentada a seguir:

class PanelExample(SimplePanel):
    def __init__(self):
        SimplePanel.__init__(self)
     
        hPanel = HorizontalPanel(BorderWidth=0,
                        HorizontalAlignment=HasAlignment.ALIGN_CENTER,
                        VerticalAlignment=HasAlignment.ALIGN_MIDDLE,
                        Width="110px",
                        Height="20px")
         
        newButton = Button("Click Me",
                        getattr(self, "onNewButtonClick"))
     
        newToggleButton = ToggleButton("Up", "Down",
                        getattr(self, "onToggleButtonClick"))
     
        hPanel.add(newButton)
        hPanel.add(newToggleButton)
        self.add(hPanel)

    def onNewButtonClick(self):
        Window.alert("New Button Selected ")

    def onToggleButtonClick(self):
        Window.alert("Toggle Button Selected")

Por fim, é necessário implementar uma classe que possua a função onModuleLoad() que incluirá os paineis e menus criados.

class ControlExample:
    def onModuleLoad(self):
        menubar = MenubarExample()
        RootPanel().add(menubar)
     
        panel = PanelExample()
        RootPanel().add(panel)

Finalizando, criaremomos a função main.

if __name__ == '__main__':
    app = ControlExample()
    app.onModuleLoad()

Este script pode ser construído via 'pyjsbuild nome_do_script.py'. Todos os arquivos produzidos estarão no diretório 'output'. Para debugá-lo, basta inserir o parametro '-d' no comando acima.

No arquivo HTML você pode incluir o CSS para melhorar a aparência de sua página. Um exemplo seria,

.gwt-MenuBar {
   background-color: #FFF;
   border: 1px solid #333;
   cursor: default;
}

.gwt-MenuBar .gwt-MenuItem {
   padding: 1px 4px 1px 4px;
   font-size: smaller;
   cursor: default;
}

.gwt-MenuBar .gwt-MenuItem-selected {
   background-color: #EEE;
}

A saída produzida por nosso código, juntamente com o CSS acima, pode ser mostrada na figura abaixo.

Barra de Menus, um botão comun e um toggle.
Concluíndo, procuramos introduzir neste artigo  uma ferramenta extremamente prática na construção de sites mais dinâmicos ou sistemas Web. Com a utilização do framework Pyjamas, o desenvolvedor ganha muito mais tempo no desenvolvimento das suas aplicações.

Para quem gosta da linguagem Python e de desenvolvimento Web, vale a pena experimentar o Pyjamas.


Referências


Monday, January 30, 2012

Uma visão básica e essencial sobre Autotools - Parte II


Nessa segunda parte veremos como gerar um makefile a partir do comando Automake, inserir bibliotecas e outros comandos e informações necessárias.

Na Parte I, geramos um script que verifica as dependências e configurações do sistema para a compilação dos códigos-fonte: o 'configure'. A partir da execução desse script, temos a geração do Makefile e assim podemos compilar nosso código.

Antes de continuarmos, vamos sanar o problema da falta do arquivo "config.h.in". Para isso, executamos simplesmente,

autoheader

Verificamos que, se executarmos o script 'configure' novamente, o erro "config.status: error: cannot find input file: `config.h.in'" não será exibido e ao invés disso, ele criará o arquivo "config.h".

Tal arquivo é o que garante a portabilidade do código no momento da compilação. Nele é possível colocar as definições necessárias para as diretivas de compilação, por exemplo.


Gerando um Makefile a partir do Automake

Uma das características mais interessantes dessas ferramentas, na minha opinião, é justamente essa: poder gerar um makefile completo e de forma automática. Primeiramente, devemos incluir o comando no nosso script que indica a criação de um Makefile automatico. Portanto, no arquivo "configure.ac" incluímos o seguinte trecho,

...
C_PREREQ([2.67])
AC_INIT([FULL-PACKAGE-NAME], [VERSION], [BUG-REPORT-ADDRESS])
AM_INIT_AUTOMAKE
AC_CONFIG_SRCDIR([src/helloworld.c])
AC_CONFIG_HEADERS([config.h])
...

Continuando, devemos agora renomear nosso simples Makefile criado inicialmente para 'Makefile.in',

mv Makefile Makefile.in

Ou seja, nosso makefile servirá como input para a criação do novo arquivo. E, antes de executar o comando principal, executamos,

aclocal

A execução cria o arquivo aclocal.m4 que por sua vez une todas as macros necessárias para a compilação, inclusive as definidas pelo programador, em um único arquivo.

Criamos o arquivo 'Makefile.am' para especificar os parametros a serem definidos pelo Automake. Abaixo um exemplo para nosso programa "hello world".

# Exemplo de Makefile.am

bin_PROGRAMS=helloworld
helloworld_SOURCES=src/helloworld.c

CLEANFILES=./*.o \
        helloworld

Enfim, executamos,

automake --add-missing --copy

Os parametros "--add-missing" e "--copy" fazem com que o automake copie arquivos necessários como o "install-sh" e "missing" que ainda não estão presente no nosso projeto.

A Saída da execução do comando em questão identificará a falta de alguns arquivos. A simples criação deles resolve o problema!

touch ./NEWS ./README ./AUTHORS ./ChangeLog

Os nomes dos arquivos são bem sugestivos às suas reais finalidades.


Incluindo bibliotecas específicas

Muitas vezes, nosso projeto necessita da importação de algumas bibliotecas e como elas são inseridas na configuração e geração do Makefile? A resposta é continuação desse tutorial.

Iremos utilizar duas bibliotecas a "math.h" e as bibliotecas da Glib.

Começando pela "math.h", alteremos o código do helloworld.c na pasta 'src/'.

// helloworld.c com Math.h

#include <stdio.h>
#include <math.h>

#define PI 3.1415926

int main(int argc, char *argv[]) {

float x = sin(PI/6);

printf("Seno de 60: %f\n", x);

return 0;
}

Inicialmente, devemos abrir o arquivo "configure.ac" e inserir o seguinte trecho em vermelho.

...
# Checks for libraries.
AC_CHECK_LIB(m, sin)
...

Lembre-se que quando compilamos, passamos o parâmetro "-lm" para incluir a "math.h". Logo o "m" representa tal parametro. A segunda entrada checa se existe a função "sin()" na biblioteca verificada.

Salve o arquivo "configure.ac" e execute,

autoreconf
make
./helloworld

Agora, a biblioteca "math.h" pode ser usada tranquilamente no projeto.

E se por um acaso, as libs estão em pastas específicas? Você pode adicioná-las antes da checagem acima usando,

LDFLAGS = "$LDFLAGS -L/local_da_sua_lib/"
CFLAGS = "$CFLAGS -I/local_da_sua_lib/"

No caso da Glib, quando pretendemos compilar um código-fonte que faz o uso da biblioteca usamos comumente o pkg-config passando o parametro: `pkg-config --cflags --libs glib-2.0`.

Para configurá-lo adequadamente devemos inserir o seguite código no arquivo 'configure.ac',

...
AC_CONFIG_HEADERS([config.h])

# Check libraries with pkg-config
PKG_CHECK_MODULES(GLIB, glib-2.0)

# Checks for programs.
AC_PROG_CC
...

Dessa forma, o script 'configure' irá verificar as dependências e adicionar o parâmetro citado automaticamente no Makefile gerado.

Por fim, toda a configuração necessária para incluir uma biblioteca na configuração do Automake usando duas maneiras diferentes de inclusão foi demonstrada acima.


O diretório autom4te.cache

Como fora dito na Parte I, nota-se que quando é executado o 'autoconf' uma pasta chamada autom4te.cache é criada. O intuito dela é guardar informações para que a execução de outras ferramentas como o 'autoheader' e o 'automake' seja facilitada. Ou seja, quando reconfiguramos alguns arquivos referentes ao 'automake' não é necessário executar toda vez o 'autoconf' porque os dados necessário já estão salvos (cached) no diretório 'autom4te.cache'. A sua presença não é vital para o projeto, mas a sua remoção força a execução do 'autoconf' toda vez que um comando precisar dos dados de saída do mesmo.


Conclusão

O Tutorial apresentado foi uma visão extremamente básica sobre a ferramenta GNU Autotools para que qualquer desenvolvedor interessado possa já configurar seu projeto facilmente e torná-lo portável. Há materiais na literatura e na internet de fácil acesso que abordam assuntos mais aprofundados. Inclusive, o próprio manual da ferramenta (como exemplo o Autoconf Manual) é um excelente guia de estudo avançado.


Referências Extras

Friday, October 21, 2011

Uma visão básica e essencial sobre Autotools - Parte I


O que é o Autotools?

O Autotools ou, como também é chamado, GNU build system é comumentemente utilizado por desenvolvedores de software livre para tornar seu programa bastante portável e, principalmente, para prover uma automatização ao projeto.
Com o GNU Make já é possível obter uma automatização sensível na elaboração e construção de qualquer projeto, entretanto, quando usamos o GNU Automake até o próprio arquivo de Makefile é gerado automaticamente, ou seja, não é mais necessário gastar tempo na codificação de um arquivo Makefile, por exemplo.
A Ferramenta GNU Autoconf é mais interessante ainda. Com ela é possível gerar scripts que configuram seu projeto de forma automática, adaptando-o ao sistema que está sendo usado e verificando as dependências necessárias.


Colocando a mão na massa!

Neste post, abordaremos uma visão simples e fácil para utilizarmos o Autotools em nossos projetos.

Antes de iniciar, devemos baixar os pacotes essenciais. Com um simples apt-get (para sistemas Debian-like) resolvemos o problema.

$ sudo apt-get install autoconf automake

Depois de instalados, podemos iniciar a automatização de nosso projeto com um "Hello World!" básico a seguir,

// helloworld.c
#include <stdio.h>

int main(int argc, char *argv[]) {

printf("Hello world!\n");

return 0;
}

Para dificultar colocaremos o código-fonte em uma pasta chamada "src" e na pasta base criaremos o makefile a seguir,

#makefile

all:
gcc -o helloworld src/helloworld.c

clean:
rm -r -f helloworld *.o src/*.o

Portanto, já temos nosso código base na pasta "src" e o makefile na pasta raiz. Agora iniciaremos o uso do GNU Autotools. Para isso, realizaremos todas as operações na pasta raiz (onde se encontra o makefile). Portanto, no shell devemos efetuar primeiramente,

$ autoscan

Gerando dois arquivos: o autoscan.log e o configure.scan. Devemos renomear esse útlimo para que ele sirva como entrada para geração do script de configuração pelo comando autoconf.

$ mv configure.scan configure.ac

e por fim,

$ autoconf

Notemos que foi criado um script chamado "configure" e uma pasta chamada "autom4te.cache". Veremos mais a frente o porquê esta pasta é necessária.

Antes de executarmos nosso script devemos renomear o nosso makefile para "makefile.in". Afinal, ele será um input para o script gerar o makefile.

mv makefile makefile.in

Já podemos então executar o script configure.

./configure

A saída esperada será:

checking for gcc... gcc
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables...
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether gcc accepts -g... yes
checking for gcc option to accept ISO C89... none needed
configure: creating ./config.status
config.status: creating makefile
config.status: error: cannot find input file: `config.h.in'

Notemos que ele não achou o arquivo de entrada "config.h.in", porém ele gerou o makefile.
Logo, podemos executar esse script em qualquer máquina para gerar o makefile automaticamente, verificando as dependências.

Agora basta um simples comando make para compilar e gerar o binário desejado.

Por fim, chegamos aos primeiros passos da utilização do GNU Autotools, mais a frente iremos aprender novos conceitos, inserir bibliotecas e gerar um makefile a partir do Automake.


Referências

Algumas Referências interessantes sobre Autotools:

http://www.lrde.epita.fr/~adl/autotools.html
http://markuskimius.wikidot.com/programming:tut:autotools

Tuesday, August 23, 2011

How to Develop a Cairo Clock using Common Lisp and GTK+

A Short Introduction

In my last project based on Lisp and GTK+ Toolkit, I need to use some graphics to build a drawing module. Then, the best solution found was the use of cairo libraries to render this kind of graphics. The Cairo API provides anti-aliasing and a simple handling.  However, I didn't find some books or tutorials that show how I could do it. With some searchs at Google, I found a tutorial made by the author of the Cairo bindings for Common Lisp, but the article didn't show an Cairo integration with Gtk+.

Then, in our laboratory, we understood the source code of the Cairo bindings for Lisp and built a solution to implement a application Lisp based, using Gtk+ and Cairo.

If you interested yourself or only want to learn the Common Lisp Language, I suggest the on-line book: "Practical Common Lisp". A suggestions for people who want to start programming Lisp is download, or a Eclipse plug-in called Cusp, or use Emacs running Slime. You can install the essentials packages using the command line,

$ sudo apt-get install emacs23 sbcl slime cl-alexandria cl-asdf cl-babel cl-cffi cl-closer-mop cl-clg cl-trivial-features cl-swank

The source packages of cl-cairo2 can be found at Cliki cl-cairo2 project. The installation is so easy, you need to extract the source and paste it in ~/.sbcl/site/. After create the symbolic links of all .asd,

$ ln -s ~/.sbcl/site/YOUR_CAIRO_FOLDER/*.asd ~/.sbcl/systems

Now, you're ready to start developing common lisp, gtk+ and cairo applications.


Developing Lisp Apps using Cairo and Gtk+

First you need to import the libraries using Asdf,

(asdf:load-system :cl-gtk2-gtk)
(asdf:load-system :cl-cairo2)
(asdf:load-system :cl-cairo2-x11)

The assignment of cairo context can be made with,

...
(let ((cr (make-instance 'cl-cairo2:context
                     :pointer cr-p
                     :width draw-w
                     :height draw-h
                     :pixel-based-p t)))
             (setq cl-cairo2:*context* cr)
             (cl-cairo2:rectangle 0 0 500 500 cr)
             (cl-cairo2:clip cr)
             (draw-clock cr)))))
...

The function that draws the cairo clock is,

;; Function to create the clock from a Cairo context
(defun draw-clock (context)
   (setq radius 180)
   (cl-cairo2:set-source-rgb 1 1 1 context)
   (cl-cairo2:arc (/ draw-h 2) (/ draw-w 2) (/ radius 2) 0 (* 2 pi) context)
   (cl-cairo2:fill-path context)
   (cl-cairo2:set-source-rgb 0 0 0 context)
   (cl-cairo2:arc (/ draw-h 2) (/ draw-w 2) (/ radius 2) 0 (* 2 pi) context)
   (cl-cairo2:stroke context)

   ;; 0 to 11 hours
   (loop for i from 1 to 12 do
          ...
   )
)

Then, we finished an example of using Cairo and Gtk on Lisp.
You can download the source code here: cairoclock.lisp


Thursday, August 4, 2011

Criando um Servidor Git usando Gitweb, Gitosis e o próprio Git

Todos nós desenvolvedores, um dia, utilizaremos ou, senão, já usamos Controles de Versões de projetos. Nesse post, eu comentarei um pouco sobre a criação de um servidor Git particular para ser usado como repositório. Além disso, indicarei possíveis erros que possam ocorrer durante a criação do mesmo.

Primeiramente devemos instalar no servidor os pacotes necessários.

$ sudo apt-get install git-core gitosis gitweb


Aplicando o Gitosis

Com os pacotes já instalados, recomendamos que seja criado um usuário no servidor chamado 'git' exclusivo e sem senha,

$ sudo adduser \
  --system \
  --shell /bin/bash \
  --gecos 'Git Version Control' \
  --group \
  --disabled-password \
  --home /home/git \
  git

 Criado o usuário, vamos ter como resposta,

Adding system user 'git'...
Adding new group 'git' (211).
Adding new user 'git' (211) with group 'git'.
Creating home directory '/home/git'.

Como queremos acessar o usuário 'git' sem senha, criaremos uma chave RSA pública em nossa própria máquina e não no servidor. Primeiramente, devemos criar (se ela ainda não existir) uma pasta chamada .ssh em /home/git/ com,

$ mkdir .ssh

Já em nossa máquina devemos criar a chave pública fazendo,

$ ssh-keygen -t rsa

e compiamos ela para a pasta /tmp/ do servidor,

$ scp ~/.ssh/id_rsa.pub seu_login@servidor_hostname:/tmp/

Feito isso, liberamos o acesso com,

$ sudo cat /tmp/id_rsa.pub >> /home/git/.ssh/authorized_keys

Agora podemos acessar via SSH nosso usuário Git sem senha. Para começarmos a utilizar o Gitosis, devemos efetuar o seguinte comando no home folder do usuário git,

$ sudo -H -u git gitosis-init < id_rsa.pub

Sendo a saída esperada justamente duas vezes a mesma mensagem,

Initialized empty Git repository in ./
Initialized empty Git repository in ./

Devemos habilitar o script para execução do git update-server-info com,

$ sudo chmod 755 /home/git/repositories/gitosis-admin.git/hooks/post-update

Na sua máquina agora será possível realizar um git clone via SSH.

$ git clone git@servidor_hostname:~/repositories/gitosis-admin.git
$ cd gitosis-admin.git

Você, primeiramente, irá clonar um repositório vazio e, posteriormente, poderá inserir arquivos a serem adicionados ao commit.

Para adicionar permissões, é possível editar o arquivo encontrado em /home/git/repositories/gitosis-admin.git/gitosis.conf no servidor. Editando-o da seguinte maneira,

[gitosis]

[group gitosis-admin]
writable = giosis-admin
members = nome_do_usuário

Para adicionar mais grupos, repositórios ou membros, podemos exemplificar com o modelo abaixo,

[group laboratório]
writable = projeto
members = usuário1 usuário2 usuário3

* Importante: Se optarmos por garantir o acesso público utilizando o protocolo Git, devemos efetuar,

$ touch ~/repositories/gitosis-admin.git/git-daemon-export-ok

e iniciaremos o daemon com,

$ sudo -u git git daemon --base-path=/home/git/repositories --export-all

Assim, podemos usar o git clone efetuando,

$ git clone git://servidor_hostname/gitosis-admin.git

Devemos lembrar que o protocolo Git é usado somente leitura. Portanto em casos de git push essa URL não será válida levando a erros que serão reportados em outros posts.


Criando outros Repositórios de Projetos

Podemos criar pastas no mesmo diretório que o gitosis-admin. No meu caso, como utilizarei o Gitweb criarei uma outra pasta dentro de /home/git/repositories/, chamada de "projects" e dentro de "projects" criarei o "meuprojeto".

$ cd /home/git/repositories/
$ mkdir projects
$ cd projects
$ mkdir meuprojeto

Para fazer com que "meuprojeto" seja um repositório git, devemos realizar os seguintes comandos,

$ cd meuprojeto
$ git --bare init
$ cp ~/repositories/gitosis-admin.git/hooks/post-update /hooks/
$ touch ~/repositories/projects/meuprojeto/git-daemon-export-ok

Voltando a máquina local, podemos cloná-lo para adicionar os arquivos referentes ao projeto. Obviamente, temos a liberdade de fazer isso diretamente no servidor, entretanto, essa é uma forma que eu acredito ser mais organizada e mais lógica. Portanto, efetuaremos o seguinte comando,

$ git clone git://servidor_hostname/projects/meuprojeto

Uma mensagem de alerta aparecerá, informando que o repositório git clonado está vazio. Uma mensagem absolutamente normal.

Podemos copiar todos os arquivos referentes, efetuando, por exemplo,

$ cd meuprojeto
$ git add .
$ git commit -a -m "Qualquer informação válida sobre o commit"
$ git push origin master

O git push origin master irá realizar um commit inicial no branch master da origem que é o servidor. É muito importante realizar esse primeiro comando no primeiro push.

* Importante: Após realizarmos essas alterações no projeto, o mais lógico seria enviá-las ao servidor como forma de atualização do repositório do projeto, mas quando efetuamos o comando acima,

$ git push origin master

Uma mensagem como essa aparecerá,

fatal: The remote end hung up unexpectedly

Isso acontece graças ao clone realizado com o protocolo git, na qual, é um protocolo usado somente para leitura e não para a escrita de dados vindos do servidor. Então, quando efetuamos um push ele tentará acessar a URL usada no clone que será git://servidor_hostname/projects/meuprojeto, não obtendo sucesso. Para corrigirmos essa situação, temos a opção de atualizar as configurações do nosso projeto verificadas em,

$ git config --list

podemos observar o campo remote.origin.url="..." como sendo igual a URL clonada. Para alterarmos, basta executar um editor e alterar esse campo com a URL destinada ao SSH.

$ vim .git/config

e editarmos o campo,

...
[remote "origin"]
fetch = +refs/heads/*:refs/remotes/origin/*
url = git@servidor_hostname:~/repositories/projects/meuprojeto
...

Agora, podemos executar o git push,

$ git push origin master

Não iremos cobrir nesse post a configuração do Apache para assim usarmos o protocolo HTTP em clones de repositórios. Futuramente, iremos abordar melhor como fazer tal procedimento.

Finalizando, por questões de segurança, podemos limitar o acesso via SSH ao usuário Git fazendo,

$ chsh -s /usr/bin/git-shell

Terminamos aqui uma configuração básica e essencial de um servidor Git utilizado para hospedar projetos e trabalhos.


Configurando o Gitweb

A configuração do Gitweb é relativamente fácil. Procedemos,

$ sudo mkdir /var/www/git

Criada a pasta, configuremos o Git no Apache,

$ sudo vim /etc/apache2/conf.d/git

preenchendo, então, o arquivo com os seguinte dados:

<Directory /var/www/git>
        Allow from all
        AllowOverride all
        Order allow,deny
        Options ExecCGI
        <Files gitweb.cgi>
                SetHandler cgi-script
        </Files>
</Directory>

O Próximo passo é mover todos os arquivos provenientes do Gitweb para a nova pasta criada.

$ sudo mv /usr/share/gitweb/* /var/www/git/
$ sudo mv /usr/lib/cgi-bin/gitweb.cgi /var/www/git/

Para terminarmos a configuração, editemos o arquivo de configuração do Gitweb,

$ sudo vim /etc/gitweb.conf

como o exemplo abaixo (que segue o exemplo da criação do repositório "meuprojeto"),

# path to git projects (<project>.git)
$projectroot = "/home/git/repositories/projects/";

# directory to use for temp files
$git_temp = "/tmp";

# target of the home link on top of all pages
#$home_link = $my_uri || "/";

# html text to include at home page
$home_text = "indextext.html";

# file with project list; by default, simply scan the projectroot dir.
$projects_list = $projectroot;

# stylesheet to use
$stylesheet = "gitweb.css";

# javascript code for gitweb
$javascript = "gitweb.js";

# logo to use
$logo = "git-logo.png";

# the 'favicon'
$favicon = "git-favicon.png";

Terminamos assim a configuração do Gitweb e podemos acessar nossos projetos, seus commits, diffs via página HTML.


Problemas que podem ocorrer durante a configuração do Servidor

Alguns problemas já foram descritos acima, como problemas decorrentes do protocolo Git que se comporta como somente leitura sendo corrigida.

Um problema enfrentado quando eu solicitei um git clone foi o bloqueio da porta 9418 (utilizada pelo git daemon) recebendo um,

fatal: unable to connect a socket (Connection timed out)

Para liberarmos o firewall para utilizarmos o Git, no meu caso eu abilitei a porta 9418, no iptables, da seguinte maneira,

$ sudo vim /etc/init.d/iptables

Adicinando a seguinte linha ao Documento,

# git
iptables -A INPUT -p tcp -s 0/0 -d 0/0 --destination-port 9418 -j ACCEPT

É sempre bom documentarmos para quem estamos adicionando uma liberação, isso ajuda outros utilizadores do servidor em caso de alguma manutenção ou outros usos. Documentação é essencial pra qualquer prática de programação.

Por fim reiniciaremos o firewall aplicando as mudanças com,

$ sudo /etc/init.d/iptables restart

Quem seguiu as práticas citadas acima, verificou exatamente tudo, prestou a atenção em cada linha de comando, mas ainda não consegue realizar um clone ou um push, pode analisar os logs mantidos pelo git daemon. Esse arquivo, para mim, foi uma peça chave para identificar problemas relacionados a localização dos repositórios e a erros relacionados a conexão. Para analisarmos esse arquivo, realizemos um,

$ sudo cat /var/log/git-daemon/current

Na qual a minha saída no shell foi,

2011-08-01_12:36:40.42770 git-daemon starting.

Mas há todo o tipo de mensagem que envolva o git-daemon.


Considerações Finais

Não há muita complexidade para criarmos um servidor de repositórios Git em um servidor pessoal, entretanto algumas dificuldades são providas com base em outros serviços que podem influenciar na conexão, ou os próprios protocolos muitas vezes merecem um estudo antes de serem usados como o protocolo Git. Erros também aparecem devido a erros de sintaxe no momento da configuração do servidor, por isso, é sempre bom estarmos atentos aos procedimentos.

As referências usadas no auxílio da elaboração desse post são listadas a seguir, bem como um tutorial básico da utilização do Git:

[Tutorial de Git] - Tutorial elaborado por mim que abrange o essencial de Git.

[scie.nti.st] - Tutorial bem bacana sobre Git Server.
[noroyalties.org] - Um outro tutorial usado na referência.
Pro Git - Professional Version Control - Livro on-line interessante a respeito de Git.