Archivo

Entradas Etiquetadas ‘servidor’

Instalar y configurar un servidor LAMP

Lunes, 17 de Octubre de 2011 Gaspar Fernández Sin comentarios

Lámpara de bajo consumoEn esta ocasión el servidor LAMP a instalar corresponderá con Linux + Apache + MySQL + PHP, intentaré dejarlo todo funcionando.

Éste es el servidor local que suelo utilizar para mis proyectos web, de todas formas no me quiero meter en necesidades muy avanzadas, ni de seguridad (es un servidor local para hacer pruebas, desarrollar webs, y correr servicios locales; no un servidor de producción), si el servidor está cerrado al exterior, mejor. Esta guía dejará el servidor operativo, lo que hagamos con él, es cosa nuestra.

En este tutorial tocaré los siguientes puntos:

Instalación de software

En este punto, supongo que tenéis una máquina GNU/Linux instalada, si necesitas ayuda para instalar una distribución, puedes probar con este enlace.
Esta parte, será especial para los derivados de Debian o Ubuntu, ya que el nombre de los paquetes es especial para dicha distribución. Lo instalamos desde terminal:

$ sudo apt-get install libapache2-mod-php5 php5-gd libgd-tools php5-curl php5-mcrypt php-pear mysql-server php-mysql

Con esto se instalarán más paquetes y dependencias que serán necesarios para nuestro servidor, además, cuando se esté instalando el servidor MySQL nos preguntará qué contraseña queremos para root:
Root MySQL
No tiene que ser la misma que root de nuestro sistema, será una clave para controlar todas las bases de datos del servidor.

apache2: Could not reliably determine the server’s fully qualified domain name, using 127.0.1.1 for ServerName

Si nada más instalar tu servidor web dice esto, pasar no pasa nada, pero molesta un poco que de un fallo, para ello tenemos que editar /etc/apache2/httpd.conf y escribir lo siguiente:

ServerName localhost

Reiniciamos el servidor y ya está todo correcto:

$ sudo service apache2 restart

Creando el directorio de trabajo

Yo utilizo esto para tener las webs alojadas en /home, ya que lo tengo montado en la partición más grande de mi disco duro. Aquí tenemos varias opciones:

  • En el directorio personal de nuestro usuario principal (es rápido y no está mal si sólo nosotros usamos el ordenador). Por ejemplo /home/usuario/proyectos/www/
  • Crear un directorio en /home; por ejemplo /home/servidor/www/
    • Tras ello, debemos darle privilegios de escritura al usuario con el que vamos a trabajar.
    • O por el contrario, si trabajarán varios usuarios, podemos crear un grupo (por ejemplo, desarrolloweb), dar privilegios de escritura al grupo y añadir todos los usuarios a ese grupo.

      $ sudo groupadd desarrolloweb
      $ sudo mkdir -p /home/servidor/www
      $ sudo chgrp -R desarrolloweb /home/servidor/www
      $ sudo gpasswd -M usuario1,usuario2,usuario3,…,usuarioN desarrolloweb

  • Crear un usuario (desarrolloweb) y un grupo (desarrolloweb) y dar privilegios a los usuarios que vayan a acceder como en el apartado anterior.
  • Utilizar el mismo usuario con el que se ejecuta apache (apache, httpd, www-data, depende del sistema, podemos mirar el archivo /etc/passwd para ver todos los usuarios). Para ese usuario crear un directorio en /home, asignar el grupo a desarrolloweb e introducir usuarios en ese grupo:

    $ sudo mkdir -p /home/servidor/www/
    $ sudo usermod -d /home/servidor/
    $ sudo chown -R www-data:desarrolloweb /home/servidor
    $ sudo gpasswd -M usuario1,usuario2,usuario3,…,usuarioN desarrolloweb

    Este último método puede sernos útil cuando nuestros scripts escriban en archivos dentro de nuestro árbol, siempre que esos directorios tengan a www-data como usuario

  • Normalmente, nuestra instalación de apache creará también un grupo llamado www-data (o apache, o httpd, etc), por lo que podemos añadir a todos los usuarios, en lugar de a desarrolloweb a www-data.
  • Se nos pueden ocurrir muchas más formas de organizarnos.

Nota: si optamos por alguna configuración de grupo, tenemos que dar permisos 775 a los directorios del servidor. Y cuando queramos que algún archivo sea modificado/creado por el servidor web debemos darle permiso al este servidor. Si los usuarios que pueden acceder pertenecen al mismo grupo que el servidor web (recordad, www-data, httpd, apache…) con dar permiso al grupo basta, si no, tendremos que modificar los permisos del fichero para permitir a otros la escritura (chmod o+w archivo). Ahora un truco, cuando estemos creando archivos desde terminal, con un usuario perteneciente a desarrolloweb podemos hacer lo siguiente:

$ newgrp desarrolloweb
$ umask 0002

Con esto conseguimos que todos los ficheros que creemos en la sesión pertenezcan al grupo desarrolloweb y tengan los permisos 664 (lectura y escritura para propietario y grupo y lectura para otros) y los directorios, 775 (lectura, escritura y ejecución (o entrada) para propietario y grupo, lectura y entrada para otros).

Una vez tenemos el directorio de trabajo creado, podemos crear enlaces a el dentro de los directorios personales de los usuarios que van a trabajar para que tengan un acceso más rápido, por ejemplo, para mi usuario, crearé un enlace a /home/gaspy/proyectos/www:

$ ln -s /home/servidorweb/www /home/gaspy/proyectos/www

Cambiando el directorio de trabajo en el servidor web

Una vez tenemos nuestro directorio de trabajo, tenemos que decirle a Apache que lo use, por defecto utilizará /var/www ; queremos utilizar nuestro directorio de trabajo. Los valores por defecto no los tocaremos, por si algún día queremos utilizarlos como plantilla ( y por si alguna actualización los re-escribe). El método que muestro es más o menos general, aunque tal vez dependiendo de la distribución varíe algún nombre de archivo:

$ cd /etc/apache2
$ sudo a2dissite 000-default

La utilidad a2dissite elimina el enlace 000-default (dentro de /etc/apache2/sites-enabled/) a sites-available/default, por lo que copiaremos ese último y crearemos otro enlace en sites-enabled para que sea nuestro sitio principal (ayudándonos de la utilidad a2ensite). Tanto a2dissite como a2ensite son dos scripts que nos facilitan un poco la vida a la hora de manejar esos enlaces.

$ sudo cp sites-available/default sites-available/trabajo
$ sudo a2ensite trabajo
$ sudo nano sites-available/trabajo

Ahora debemos modificar algunas líneas de ese documento relativas a la ruta de trabajo, sustituyendo donde ponga /var/www por /home/servidorweb/www dejándolas así:

DocumentRoot /home/servidorweb/www
<Directory /home/servidorweb/www/>
Options Indexes FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
allow from all
</Directory>

Aunque más adelante modificaremos alguna línea más en este archivo (podemos hacerlo todo seguido, pero para este artículo prefiero indicarlo paso a paso), salvamos y salimos (en nano, Control-X).

Probando el servidor

Vamos a introducir un archivo index.php en /home/servidorweb/www/ para que se vea cuando accedemos al raíz de nuestro servidor. Sólo por hacer la prueba, puedes saltar este paso si quieres. El archivo contendrá (debéis sustituir UTF-8 por la codificación con la que creéis el archivo, y la zona horaria Europe/Madrid por la zona horaria donde estéis):

1
2
3
4
5
<?php
   date_default_timezone_set('Europe/Madrid');
   echo "<h1>Funciona!</h1>";
   echo '<p>'.htmlentities('El servidor está funcionando. Son las: '.date('H:i').' del día '.date('d/m/Y'), ENT_COMPAT, 'UTF-8').'</p>';
?>

Para ver el resultado, si estáis en el mismo ordenador donde habéis instalado el servidor, acceded a http://localhost/ , si no, acceded a través de la IP de la máquina servidor, debería dar un mensaje como este:
Server funcionando

Permitir cambiar las opciones de directorios con .htaccess

Es muy útil si vamos a utilizar mod_rewrite, contraseñas en directorios o modificar filtros, documentos de error, etc. aunque por defecto viene deshabilitada cualquier configuración que podamos hacer desde aquí. Para ello, debemos abrir el mismo archivo de antes /etc/apache2/sites-available/trabajo para edición (igual que antes, y cambiar la directiva AllowOverride de None a All (dentro de Directory /home/servidorweb/www), al final se quedaría así:

<Directory /home/servidorweb/www/>
Options Indexes FollowSymLinks MultiViews
AllowOverride All
Order allow,deny
allow from all
</Directory>

Seguramente haya otra etiqueta <Directory>, en este caso ““, con otra directiva AllowOverride que podemos dejar a None, esto es para cuando haya un archivo .htaccess en un nivel superior a nuestro directorio de trabajo, por ejemplo en /home/servidorweb; si ponemos la directiva a “All”, sí que se procesará ese archivo, aunque en este caso, si va a ser una configuración común a todo el servidor (ya que afectará a todos los subdirectorios que tengamos exportados) podemos ponerla en /etc/apache2/sites-available/trabajo.

Configurar mod-rewrite

$ sudo a2enmod rewrite

Ejecuta un script que habilita el módulo especificado. Para ver un listado de los módulos especificados podemos mirar en el directorio /etc/apache2/mods-available ; este script hará un enlace de un módulo en /etc/apache2/mods-enabled. Desde aquí podemos activar el módulo que queramos. Para desactivar ejecutamos como root a2dismod. De forma muy parecida a como hicimos con el sitio web.

Instalamos phpmyadmin

Podemos instalarlo automáticamente con nuestra distribución, por ejemplo en Debian/Ubuntu y derivados:

$ sudo apt-get install phpmyadmin

Nos pedirá la contraseña de la base de datos y listo.

Personalmente, no me gusta que mi distribución haga ciertos cambios y este es uno de ellos, me gusta hacer la instalación de phpMyAdmin de forma manual, así como la de ciertos scripts y utilidades del servidor… en ocasiones edito algún archivo de configuración por nfs y tengo un acceso directo a ellos, además, me gusta complicarme un poco la vida :)

Lo primero es descargarlo, de la web oficial. La última versión hoy es la 3.4.5 (bonito número) por tanto lo instalaremos. Primero creo un directorio para las utilidades de mi servidor local, y tras ello descomprimo ahí phpmyadmin; el archivo comprimido de phpMyAdmin se encuentra en /home/servidorweb/targz:

$ cd /home/servidorweb
$ mkdir util
$ cd util
$ tar xvzf ../targz/phpMyAdmin-3.4.5-all-languages.tar.gz

Se descomprimirá todo en el directorio phpMyAdmin-3.4.5-all-languages/ que renombraremos a pma (PhpMyAdmin), aunque el nombre es lo de menos, sólo queremos un acceso fácil:

$ mv phpMyAdmin-3.4.5-all-languages pma

A continuación vamos a dar acceso al servidor web al directorio /home/servidorweb/util/ ya que está fuera de /home/servidorweb/www/ que es el que tiene acceso desde web. Para ello creamos un nuevo archivo en /etc/apache2/conf.d/ llamado, por ejemplo utilidades.conf, y contendrá lo siguiente:

Alias /util/ “/home/servidorweb/util/”

<Directory “/home/servidorweb/util”>
AllowOverride None
Order allow,deny
Allow from all
</Directory>

Para ver los cambios recargamos la configuración del servidor:

$ sudo service apache2 reload

En este punto, si accedemos desde el navegador a nuestro servidor en la ruta donde hemos vinculado phpmyadmin, por ejemplo http://localhost/util/pma/ veremos:
pma
Ya tenemos phpMyAdmin funcionando, aunque falta un pequeño detalle (no nos impide en cierto modo trabajar, pero ya que lo instalamos lo instalamos bien). phpMyAdmin tiene funciones avanzadas como la creación de PDFs, diseñador de bases de datos, historial y algunas cosas más y para ello necesita que creemos una base de datos e instalemos algunas tablas en ella, para ello, primero crearemos el usuario pma en nuestra base de datos, lo haremos desde el comando mysql:

$ mysql -u root -p

Enter password: [introducimos la contraseña, la primera que introdujimos al principio del tutorial]
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 104

mysql> CREATE USER ‘pma’@'localhost’ IDENTIFIED BY ‘axK12jMf’
Query OK, 0 rows affected (0.03 sec)
mysql> exit

La contraseña podemos generarla con pwgen, cuando terminemos de configurar esto, no la necesitaremos para nada.
Ahora cargamos en la base de datos las tablas de phpMyAdmin (las encontramos dentro del directorio de instalación):

$ mysql -u root -p < /home/servidorweb/util/pma/scripts/create-tables.sql

Ahora damos privilegios al usuario pma para la base de datos phpmyadmin:

$ mysql -u root -p
mysql> GRANT SELECT, INSERT, DELETE, UPDATE ON `phpmyadmin`.* TO ‘pma’@'localhost’;
mysql> exit

Para terminar de configurar debemos crear el fichero config.inc.php, aunque phpMyAdmin trae un fichero llamado config.sample.inc.php que nos puede servir de plantilla. Para ello:

$ cd /home/servidorweb/util/pma/
$ cp config.sample.inc.php config.inc.php

Y editamos el archivo. Podemos ir a http://localhost/util/pma/setup/ y hacer algunos ajustes, luego podemos copiar el archivo generado en config.inc.php o, de manera manual, editando el archivo, para ello, lo primero que debemos rellenar es la línea de blowfish_secret, será una frase secreta para la autentificación de tipo cookie de la aplicación; podemos poner una frase cualquiera, lo primero que se nos ocurra, el caso es que sea secreto, podemos generarlo si queremos con:

$ pwgen -yns 46 1

El tema es que se quede algo como:

1
$cfg['blowfish_secret'] = 'Frase secreta que me acabo de inventar'; /* YOU MUST FILL IN THIS FOR COOKIE AUTH! */

Ahora, debemos rellenar las opciones del servidor MySQL, para ello (podemos copiar y pegar, aunque viene comentado en el archivo):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$cfg['Servers'][$i]['controluser'] = 'pma';
$cfg['Servers'][$i]['controlpass'] = 'CLAVE PMA';

/* Storage database and tables */
$cfg['Servers'][$i]['pmadb'] = 'phpmyadmin';
$cfg['Servers'][$i]['bookmarktable'] = 'pma_bookmark';
$cfg['Servers'][$i]['relation'] = 'pma_relation';
$cfg['Servers'][$i]['table_info'] = 'pma_table_info';
$cfg['Servers'][$i]['table_coords'] = 'pma_table_coords';
$cfg['Servers'][$i]['pdf_pages'] = 'pma_pdf_pages';
$cfg['Servers'][$i]['column_info'] = 'pma_column_info';
$cfg['Servers'][$i]['history'] = 'pma_history';
$cfg['Servers'][$i]['tracking'] = 'pma_tracking';
$cfg['Servers'][$i]['designer_coords'] = 'pma_designer_coords';
$cfg['Servers'][$i]['userconfig'] = 'pma_userconfig';

Arriba del todo, en la segunda línea, debemos cambiar el valor por la clave que le asignamos al usuario pma.
Por otra parte, si utilizamos la base de datos adicional (phpmyadmin), necesitamos utilizar la extensión mysqli de php en lugar de mysql (puede haber problemas si no lo hacemos):

1
2
/* Select mysqli if your server has it */
$cfg['Servers'][$i]['extension'] = 'mysqli';

Si queremos, podemos incluir servidores a la lista de autentificación inicial de phpMyAdmin, con lo que podremos acceder sin contraseña a nuestro servidor (como root o como el usuario que elijamos), puede ser un método un tanto inseguro, pero si sólo estamos nosotros y tenemos el servidor cerrado al exterior no hay problema. Para ello, debajo de la identificación del primer servidor (las líneas que hemos añadido), añadimos lo siguiente en config.inc.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
$i++;
$cfg['Servers'][$i]['verbose'] = 'Servidor Local';
$cfg['Servers'][$i]['host'] = 'localhost';
$cfg['Servers'][$i]['port'] = '';
$cfg['Servers'][$i]['socket'] = '';
$cfg['Servers'][$i]['connect_type'] = 'tcp';
$cfg['Servers'][$i]['extension'] = 'mysqli';
$cfg['Servers'][$i]['auth_type'] = 'config';
$cfg['Servers'][$i]['user'] = 'root';
$cfg['Servers'][$i]['password'] = 'NUESTRA CLAVE DE ROOT DE MYSQL';
$cfg['Servers'][$i]['controluser'] = 'pma';
$cfg['Servers'][$i]['controlpass'] = 'CLAVE PMA';
/* Ponemos esto de nuevo */
/* Storage database and tables */
$cfg['Servers'][$i]['pmadb'] = 'phpmyadmin';
$cfg['Servers'][$i]['bookmarktable'] = 'pma_bookmark';
$cfg['Servers'][$i]['relation'] = 'pma_relation';
$cfg['Servers'][$i]['table_info'] = 'pma_table_info';
$cfg['Servers'][$i]['table_coords'] = 'pma_table_coords';
$cfg['Servers'][$i]['pdf_pages'] = 'pma_pdf_pages';
$cfg['Servers'][$i]['column_info'] = 'pma_column_info';
$cfg['Servers'][$i]['history'] = 'pma_history';
$cfg['Servers'][$i]['tracking'] = 'pma_tracking';
$cfg['Servers'][$i]['designer_coords'] = 'pma_designer_coords';
$cfg['Servers'][$i]['userconfig'] = 'pma_userconfig';

Creando un servidor que acepte múltiples clientes simultáneos en C

Domingo, 6 de Febrero de 2011 Gaspar Fernández Sin comentarios

Para hacer una prueba de esto, crearemos un servidor al que nos podremos conectar por telnet y pedir cierta información a través de comandos. El ejemplo soporta los siguientes comandos (en mayúsculas):

  • DATE: Pide la fecha al servidor
  • TIME: Pide la hora al servidor
  • HOLA: Saluda y me dice mi IP
  • EXIT: Cierra el cliente actual
  • CERRAR: Cierra el servidor.
  • Cualquier otra cosa se manda repetida (ECHO)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
/**
*************************************************************
* @file servtcp.c
* @brief Breve descripción
* Ejemplo de un cliente TCP usando threads
*
*
* @author Gaspar Fernández <blakeyed@totaki.com>
* @version 0.1Beta
* @date 13 ene 2011
* Historial de cambios:
*   20110113 - Versión inicial
*
*
*************************************************************/


#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <netinet/in.h>
#include <resolv.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>

/** Puerto  */
#define PORT       7000

/** Número máximo de hijos */
#define MAX_CHILDS 3

/** Longitud del buffer  */
#define BUFFERSIZE 512

int AtiendeCliente(int socket, struct sockaddr_in addr);
int DemasiadosClientes(int socket, struct sockaddr_in addr);
void error(int code, char *err);
void reloj(int loop);

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

    int socket_host;
    struct sockaddr_in client_addr;
    struct sockaddr_in my_addr;
    struct timeval tv;      /* Para el timeout del accept */
    socklen_t size_addr = 0;
    int socket_client;
    fd_set rfds;        /* Conjunto de descriptores a vigilar */
    int childcount=0;
    int exitcode;

    int childpid;
    int pidstatus;

    int activated=1;
    int loop=0;
    socket_host = socket(AF_INET, SOCK_STREAM, 0);
    if(socket_host == -1)
      error(1, "No puedo inicializar el socket");
   
    my_addr.sin_family = AF_INET ;
    my_addr.sin_port = htons(PORT);
    my_addr.sin_addr.s_addr = INADDR_ANY ;

   
    if( bind( socket_host, (struct sockaddr*)&my_addr, sizeof(my_addr)) == -1 )
      error(2, "El puerto está en uso"); /* Error al hacer el bind() */

    if(listen( socket_host, 10) == -1 )
      error(3, "No puedo escuchar en el puerto especificado");

    size_addr = sizeof(struct sockaddr_in);


    while(activated)
      {
    reloj(loop);
    /* select() se carga el valor de rfds */
    FD_ZERO(&rfds);
    FD_SET(socket_host, &rfds);

    /* select() se carga el valor de tv */
    tv.tv_sec = 0;
    tv.tv_usec = 500000;    /* Tiempo de espera */
   
    if (select(socket_host+1, &rfds, NULL, NULL, &tv))
      {
        if((socket_client = accept( socket_host, (struct sockaddr*)&client_addr, &size_addr))!= -1)
          {
        loop=-1;        /* Para reiniciar el mensaje de Esperando conexión... */
        printf("\nSe ha conectado %s por su puerto %d\n", inet_ntoa(client_addr.sin_addr), client_addr.sin_port);
        switch ( childpid=fork() )
          {
          case -1:  /* Error */
            error(4, "No se puede crear el proceso hijo");
            break;
          case 0:   /* Somos proceso hijo */
            if (childcount<MAX_CHILDS)
              exitcode=AtiendeCliente(socket_client, client_addr);
            else
              exitcode=DemasiadosClientes(socket_client, client_addr);

            exit(exitcode); /* Código de salida */
          default:  /* Somos proceso padre */
            childcount++; /* Acabamos de tener un hijo */
            close(socket_client); /* Nuestro hijo se las apaña con el cliente que
                         entró, para nosotros ya no existe. */

            break;
          }
          }
        else
          fprintf(stderr, "ERROR AL ACEPTAR LA CONEXIÓN\n");
      }

    /* Miramos si se ha cerrado algún hijo últimamente */
    childpid=waitpid(0, &pidstatus, WNOHANG);
    if (childpid>0)
      {
        childcount--;   /* Se acaba de morir un hijo */

        /* Muchas veces nos dará 0 si no se ha muerto ningún hijo, o -1 si no tenemos hijos
         con errno=10 (No child process). Así nos quitamos esos mensajes*/


        if (WIFEXITED(pidstatus))
          {

        /* Tal vez querremos mirar algo cuando se ha cerrado un hijo correctamente */
        if (WEXITSTATUS(pidstatus)==99)
          {
            printf("\nSe ha pedido el cierre del programa\n");
            activated=0;
          }
          }
      }
    loop++;
    }

    close(socket_host);

    return 0;
}

    /* No usamos addr, pero lo dejamos para el futuro */
int DemasiadosClientes(int socket, struct sockaddr_in addr)
{
    char buffer[BUFFERSIZE];
    int bytecount;

    memset(buffer, 0, BUFFERSIZE);
   
    sprintf(buffer, "Demasiados clientes conectados. Por favor, espere unos minutos\n");

    if((bytecount = send(socket, buffer, strlen(buffer), 0))== -1)
      error(6, "No puedo enviar información");
   
    close(socket);

    return 0;
}

int AtiendeCliente(int socket, struct sockaddr_in addr)
{

    char buffer[BUFFERSIZE];
    char aux[BUFFERSIZE];
    int bytecount;
    int fin=0;
    int code=0;         /* Código de salida por defecto */
    time_t t;
    struct tm *tmp;

    while (!fin)
      {

    memset(buffer, 0, BUFFERSIZE);
    if((bytecount = recv(socket, buffer, BUFFERSIZE, 0))== -1)
      error(5, "No puedo recibir información");

    /* Evaluamos los comandos */
    /* El sistema de gestión de comandos es muy rudimentario, pero nos vale */
    /* Comando TIME - Da la hora */
    if (strncmp(buffer, "TIME", 4)==0)
      {
        memset(buffer, 0, BUFFERSIZE);

        t = time(NULL);
        tmp = localtime(&t);

        strftime(buffer, BUFFERSIZE, "Son las %H:%M:%S\n", tmp);
      }
    /* Comando DATE - Da la fecha */
    else if (strncmp(buffer, "DATE", 4)==0)
      {
        memset(buffer, 0, BUFFERSIZE);

        t = time(NULL);
        tmp = localtime(&t);

        strftime(buffer, BUFFERSIZE, "Hoy es %d/%m/%Y\n", tmp);
      }
    /* Comando HOLA - Saluda y dice la IP */
    else if (strncmp(buffer, "HOLA", 4)==0)
      {
        memset(buffer, 0, BUFFERSIZE);
        sprintf(buffer, "Hola %s, ¿cómo estás?\n", inet_ntoa(addr.sin_addr));
      }
    /* Comando EXIT - Cierra la conexión actual */
    else if (strncmp(buffer, "EXIT", 4)==0)
      {
        memset(buffer, 0, BUFFERSIZE);
        sprintf(buffer, "Hasta luego. Vuelve pronto %s\n", inet_ntoa(addr.sin_addr));
        fin=1;
      }
    /* Comando CERRAR - Cierra el servidor */
    else if (strncmp(buffer, "CERRAR", 6)==0)
      {
        memset(buffer, 0, BUFFERSIZE);
        sprintf(buffer, "Adiós. Cierro el servidor\n");
        fin=1;
        code=99;        /* Salir del programa */
      }
    else
      {    
        sprintf(aux, "ECHO: %s\n", buffer);
        strcpy(buffer, aux);
      }

    if((bytecount = send(socket, buffer, strlen(buffer), 0))== -1)
      error(6, "No puedo enviar información");
      }

    close(socket);
    return code;
}

void reloj(int loop)
{
  if (loop==0)
    printf("[SERVIDOR] Esperando conexión  ");

  printf("\033[1D");        /* Introducimos código ANSI para retroceder 2 caracteres */
  switch (loop%4)
    {
    case 0: printf("|"); break;
    case 1: printf("/"); break;
    case 2: printf("-"); break;
    case 3: printf("\\"); break;
    default:            /* No debemos estar aquí */
      break;
    }

  fflush(stdout);       /* Actualizamos la pantalla */
}

void error(int code, char *err)
{
  char *msg=(char*)malloc(strlen(err)+14);
  sprintf(msg, "
Error %d: %s\n", code, err);
  fprintf(stderr, msg);
  exit(1);
}

La clave para poder atender varios clientes simultáneos es bifurcar (fork()) el programa cuando se conecta un nuevo cliente, de esta forma, el diálogo con el cliente se lleva a cabo desde un nuevo proceso mientras que el proceso padre (el primero) sigue escuchando en el puerto esperando conexiones.

Utilizamos select() para no detener la ejecución del programa cuando estamos esperando conexiones; tal vez el programa tenga que hacer algo mientras nadie está conectándose (y es que el programa principal permanecerá aquí la mayor parte del tiempo), por ejemplo lo que hacemos aquí es controlar los hijos que se mueren y ver cómo se han muerto. select() establece un tiempo máximo de espera (500000 microseguntos en este caso, ver la variable tv); si pasado ese tiempo el descriptor socket_host no ha cambiado, seguiremos la ejecución normal del programa, pero si durante ese tiempo cambia ejecutaremos accept().
La función select() funciona con grupos de descriptores y por eso utilizamos las macros FD_ZERO() para limpiar el grupo y FD_SET() para añadir un descriptor al grupo.
El primer parámetro de select() debe ser el descriptor de fichero más grande a comprobar+1; como sólo tenemos un descriptor (socket_host) debemos añadir este al grupo, y pasarle a select en su primer parámetro socket_host+1.

Cuando llega un cliente, como dijimos antes, se abrirá un proceso hijo que lo procesará con una de estas dos funciones: AtiendeCliente() o DemasiadosClientes() dependiendo de si el número máximo de clientes ha sido alcanzado o no. Además, la salida del proceso hijo se obtendrá de la salida de estas funciones, siendo el valor de salida clave 99 el que necesita el proceso padre para cerrar terminar el proceso servidor, es decir, si un hijo sale con el valor de retorno 99 provocará el cierre del servidor. Este valor se maneja debajo del switch(); primero con waitpid() esperamos la muerte de alguno de nuestros hijos aunque con el modificador WNOHANG provocamos que si no se ha muerto ninguno, no vamos a esperar, el programa seguirá su ejecución.

Desde AtiendeCliente() veremos un método muy rudimentario para implementar los comandos, aunque para un ejemplo y 4 comandos nos vale perfectamente.

DemasiadosClientes() devolverá únicamente un mensaje al cliente que se acaba de conectar avisando de que hay demasiados clientes conectados.

Reloj() es un pequeño reloj en modo texto que va girando, a cada iteración del bucle principal, está aquí para que nosotros veamos que el servidor está haciendo algo. Utiliza códigos ANSI, para volver a escribirse en el lugar que estaba, y fflush() para volcar el texto a pantalla antes de nada (puede ser que se acumulen varios mensajes y entonces el dibujado sería incorrecto).

Para probar este programa, debemos compilar con:

$ gcc -o servtcp servtcp.c

y ejecutarlo; y, en un terminal aparte, conectar con telnet, Si, por ejemplo lo queremos hacer todo desde el mismo ordenador; si el servidor está en un ordenador diferente y nos queremos conectar a él cambiamos “localhost” por la IP o el host del ordenador donde se esté ejecutando el cliente:

$ telnet localhost 7000

Creando macros de escritorio I: xmacroplay

Martes, 17 de Agosto de 2010 Gaspar Fernández Sin comentarios

Voy a hablar de dos programas para crear macros, aunque hay más, claro que sí. Los dos programas están basados en la grabación de acciones del servidor X y la simulación de movimientos del ratón, clicks y acciones de teclado.

En principio xmacroplay; lo podemos ejecutar así:

$ xmacrorec2 > mi_macro

donde mi_macro es el archivo donde vamos a grabar; este archivo, si vemos su contenido tendrá los comandos en un lenguaje muy sencillo e inteligible, lo que nos permite crear macros directamente programándolos, no grabándolos.
Lo primero que debemos hacer tras ejecutar xmacrorec2 es pulsar una tecla, ésta tecla será la que deberemos pulsar para detener la grabación.

Para ejecutar las macros, lo podemos hacer de la siguiente forma:

$ xmacroplay :0 < mi_macro

donde :0 es la pantalla, el DISPLAY y mi_macro es el nombre de la macro.

Aunque, al menos en la versión 20000911, la última descargable desde sf.net (del año 2001, un poco antigua), se lleva un poco mal con los arrastres de ratón; vamos para hacer macros con clicks bien, pero cuando arrastramos algo, a veces no se reproducen bien las opciones. Se debe a un bug en xmacroplay.cpp, en las siguientes líneas dentro de la función eventLoop(), hay que añadir el código que tiene comentario

1
2
3
4
5
6
7
8
9
10
11
12
13
14
else if (!strcasecmp("ButtonPress",ev))
        {
          cin >> b;
          cout << "ButtonPress: " << b << endl;
          XTestFakeButtonEvent ( RemoteDpy, b, True, Delay );
          XFlush ( RemoteDpy ); /* AÑADIR */
}
        else if (!strcasecmp("ButtonRelease",ev))
        {
          cin >> b;
          cout << "ButtonRelease: " << b << endl;
          XTestFakeButtonEvent ( RemoteDpy, b, False, Delay );
          XFlush ( RemoteDpy );/* AÑADIR */
        }

Con esta modificación todo debe ir bien, podemos, por ejemplo programar ciertas teclas en nuestro ordenador para automáticamente grabar y reproducir macros de teclado y ratón.

Aunque como dije antes, siempre podemos programar nosotros las macros a mano, o modificar lo que hemos grabado para un mejor comportamiento con los siguientes comandos:

  • MotionNotify X Y : Mueve el rató a un punto X,Y de la pantalla
  • ButtonPress N : Pulsa el botón N del ratón (1=izquierdo, 2=rerecho, 3=centro, 4,5=rueda del ratón en algunos sistemas, etc)
  • Delay N : Retrasa el script N segundos, deteniendo su ejecución un tiempo
  • ButtonRelease N : Suelta el botón N del ratón
  • KeyCodePress C : Pulsa la tecla con el código C (podemos usar xev para ver los códigos de teclas)
  • KeyCodeRelease C : Suelta la tecla con el código C
  • KeyStrPress S : Pulsa la tecla llamada S (Es el nombre de forma de cadena de caracteres, por ejemplo “Escape”, “F5″, “Control_L”, etc; podemos utilizar xev para ver los nombres también)
  • KeyStrRelease S : Suelta la tecla llamada S

Saber si estamos conectados a internet [bash]

Miércoles, 26 de Mayo de 2010 Gaspar Fernández Sin comentarios

cat_linuxA menudo lanzo scripts que necesitan saber si la conexión a Internet está disponible para lanzarse, en caso de que no lo esté, invertirán mucho tiempo en terminar o terminarán con muchos errores. La forma más fácil de saber cuándo estamos conectados es intentar acceder a un ordenador que sepamos seguro que está conectado… por ejemplo google.com, que para eso está.

Podemos por ejemplo hacer ping al servidor, pero un ping es muy visual, necesitaremos hacer algo más para aprovechar la salida de ese ping; aunque también podemos intentar conectar por el puerto 80 (http).

Dejo varios métodos para poder hacerlo de forma sencilla desde la línea de comandos:

ping

$ if ping -c1 google.com &>/dev/null; then echo “Tienes conexion”; else echo “no tienes conexion”; fi

Con esta línea haremos un ping a google, sólo mandaremos un paquete (-c1) y mandaremos la salida de ping a /dev/null (no nos interesa lo que nos diga, queremos hacer un script amigable al usuario, sin mensajes raros), si ping no da errores, se ha hecho bien, mostramos el mensaje de tienes conexion, si no, el de no tienes conexión.

Aunque en los siguientes scripts haremos una salida de texto, siempre podremos hacer un exit y terminar el script cuando no haya conexión.

netcat

if netcat -z google.com 80; then echo “Tienes conexion”; else echo “no tienes conexion”; fi

Igual que antes pero probamos la conexión al puerto 80, netcat utiliza el parámetro -z para ver sólo si se puede conectar, una vez se conecta, se cierra la conexión.

escribiendo en el servidor directamente

Tenemos dos métodos más…

if echo “”>/dev/tcp/totaki.com/80; then echo “Tengo conexion”; else echo “no tengo”; fi

Aquí conectamos con el servidor a través de /dev/tcp (necesitamos tener permisos), al servidor le enviamos una cadena vacía.

También podemos hacer:

if exec 3>/dev/tcp/totaki.com/80; then echo “Tengo conexion”; else echo “no tengo”; fi

Utilidades y notas

Podemos utilizarlo por ejemplo en cron scripts que requieran conexión a internet (subir una copia de seguridad a un servidor externo, enviar nuestra ip de Internet a un servidor), o por ejemplo para saber la disponibilidad de un servidor, es decir, si tenemos alquilado un servidor a lo mejor nos interesa monitorizarlo para saber si está caído, en fin los usos son los que nos de la imaginación.

Antes dije que podríamos incluir un exit cuando el script no tenga conexión, pero también podremos llamar a otro script en el caso de tener conexión, o hacer lo siguiente (con el ejemplo de ping):

1
2
3
4
5
6
7
8
#!/bin/bash
if ping -c1 google.com &>/dev/null;
then
        echo "Ejecutando script";
        $@
else
        echo "no tienes conexion";
fi

Llamamos al fichero internet, damos permisos de ejecución y podremos llamar a cualquier script de la siguiente forma:

$ internet wget http://www.totaki.com/poesiabinaria

(por ejemplo), el script sólo se ejecutará si estamos conectados a Intenet

Mejoras

Bueno, puede pasar, que alguna vez google.com esté caído, podemos intentar pasar por dos o tres servidores, si vemos que uno de ellos responde (siempre que no sea de una red local) sabremos que estamos conectados.

Foto: fotohiro (Flickr)

Montando un servidor FTP en Linux

Domingo, 21 de Marzo de 2010 Gaspar Fernández Sin comentarios

Es algo que me hacía ilusión aprender y sabía que debía haber algo para hacerlo. De hecho me he encontrado con varias opciones, entre ellas: FTPFS (ya obsoleta, por culpa de LUFIS que entró para el kernel 2.6.14), pero he estado viendo una alternativa mucho mejor FUSE (Filesystem in Userspace). Éste último tiene soporte para muchísimos sistemas de archivos, se pueden montar CVS, FTP, SSH, ficheros RAR, dispositivos bluetooth, particiones NTFS (captive, ntfs-3g…), Blogger… y mucho más… Es algo que me gustaría investigar mucho más a fondo.

Lo del montaje de ftps es una cosa muy sencilla, pero por lo menos que esta entrada en el diario sirva de chuleta (tanto a mí, que cada vez que voy a hacerlo no me acuerdo de cómo es) como a muchos de vosotros.

Lo primero es tener instalado fuse, y lufis:

$ emerge sys-fs/fuse lufis

Ya estamos listos, lo bueno de este sistema es que podemos montar y desmontar siendo usuarios siempre que tengamos el módulo fuse cargado:

$ modprobe fuse

Podemos montar de la siguiente forma, siento /mnt/ftps nuestro punto de montaje (si lo hacemos como usuario, éste tiene que tener permiso sobre ese directorio:

$ lufis fs=ftpfs,host=[servidor],username=[usuario],password=[clave] /mnt/ftps

Lo bueno de esto, es que no sale la contraseña en el listado de procesos, lo malo es que sí que sale el nombre de usuario:

$ ps ax
PID TTY          STAT    TIME COMMAND

466 ?             Ss       0:00 /sbin/udevd –daemon
1599 ?             Ssl       0:00 lufis fs ftpfs host servidor username usuario password                /mnt/ftps

1941 pts/2       R+       0:00 ps ax
4012 ?             Ss       0:00 /usr/sbin/acpid -c /etc/acpi/events

Para desmontar:

$ fusermount -u /mnt/ftps

Si queremos montar un ssh… basta con cambiar ftpfs por sshfs y podemos instalar todos los sistemas de archivos que queramos, del enlace que os pasé antes y disfrutar de lo cómodo que es trabajar de esta forma.

Además esto nos permite poder hacer scripts que interactúen con un servidor FTP mucho más fácilmente.

Categories: Linux Tags: , , , ,

Identificar si estamos en el servidor local o no

Lunes, 25 de Enero de 2010 Gaspar Fernández Sin comentarios

A veces es interesante saber en qué servidor estamos, si en nuestro servidor local para hacer pruebas, o en el remoto (y será la web definitiva); puede que porque las claves de mi servidor MySQL local son diferentes, o porque en local activamos automáticamente el modo de depuración, o símplemente porque en nuestro servidor local tenemos todos nuestros proyectos como http://localhost/proyecto1/ y en el servidor se ejecutarán como http://www.proyecto1.com/.

Para ello nos vale esta línea que podemos incluir en nuestra biblioteca de funciones personal en PHP:

1
$serv_local = (file_exists($_SERVER['DOCUMENT_ROOT']."/serv_local"));

Ahora en nuestro servidor local (valga la redundancia en todo el post) hacemos un touch en nuestro DOCUMENT_ROOT (por ejemplo /home/www/public_http/):

1
2
$ cd /home/www/public_http
$ touch serv_local

Con esto siempre que ejecutemos nuestros scripts buscaremos el fichero serv_local, si se encuentra, estamos seguros de que estamos en nuestro ordenador, si no, estamos en el servidor remoto.

Categories: PHP Tags: , ,

Plantillas de correo dinámicas automáticas en Thunderbird

Domingo, 12 de Julio de 2009 blakeyed Sin comentarios
Nuevo mensaje en Mozilla Thunderbird

Nuevo mensaje en Mozilla Thunderbird

Me pareció casi increíble que Mozilla Thunderbird no pudiera incluir plantilas en los e-mails automáticamente de serie. (Es cierto que soporta plantillas, pero tienes que pulsar en la plantilla antes de crear el mail).

Afortunadamente, encontré hace tiempo la extensión ExternalTemplateLoader de Kaosmos. Nos introducir una plantilla que se cargará automáticamente cuando redactemos/respondamos/reenviemos un mensaje.  Pero además, esa plantilla podemos cargarla desde un archivo local o alojada en un servidor de Internet. Con lo cual podremos también, crear plantillas dinámicas.

Para hacer plantillas dinámicas, tenemos varias posibilidades:

  • Si lo que queremos hacer dinámico es cambiar una frase, un texto, una foto que cambie cada cierto tiempo, podemos crear un cron job que genere el archivo html de plantilla cada cierto tiempo, eso nos ahorraría tener que generar la plantilla cada vez que vamos a escribir un mail.
  • Si queremos monitorizar algo, crear un identificador por cada email que vayamos a enviar, escribir un informe, contabilizar los mails, etc; no queda más remedio que generar la plantilla cada vez que vayamos a redatar un mail.

En futuros posts incluiré ejemplos de cada uno de los métodos.

Visita otras webs de la red