Drizzled Public API Documentation

net_serv.cc
00001 /* - mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*-
00002  *  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
00003  *
00004  *  Copyright (C) 2008 Sun Microsystems, Inc.
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License
00017  *  along with this program; if not, write to the Free Software
00018  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00019  */
00020 
00021 #include <config.h>
00022 
00023 #include <drizzled/current_session.h>
00024 #include <drizzled/error.h>
00025 #include <drizzled/session.h>
00026 #include <drizzled/statistics_variables.h>
00027 #include <drizzled/system_variables.h>
00028 
00029 #include <assert.h>
00030 #include <stdio.h>
00031 #include <stdlib.h>
00032 #include <string.h>
00033 #include <signal.h>
00034 #include <errno.h>
00035 #include <sys/socket.h>
00036 #include <sys/poll.h>
00037 #include <zlib.h>
00038 #include <algorithm>
00039 
00040 #include "errmsg.h"
00041 #include "vio.h"
00042 #include "net_serv.h"
00043 
00044 namespace drizzle_plugin {
00045 
00046 using namespace std;
00047 using namespace drizzled;
00048 
00049 /*
00050   The following handles the differences when this is linked between the
00051   client and the server.
00052 
00053   This gives an error if a too big packet is found
00054   The server can change this with the -O switch, but because the client
00055   can't normally do this the client should have a bigger max_allowed_packet.
00056 */
00057 
00058   /* Constants when using compression */
00059 #define NET_HEADER_SIZE 4   /* standard header size */
00060 #define COMP_HEADER_SIZE 3    /* compression header extra size */
00061 
00062 #define MAX_PACKET_LENGTH (256L*256L*256L-1)
00063 
00064 static bool net_write_buff(NET*, const void*, uint32_t len);
00065 static int drizzleclient_net_real_write(NET *net, const unsigned char *packet, size_t len);
00066 
00069 void NET::init(int sock, uint32_t buffer_length)
00070 {
00071   vio= new Vio(sock);
00072   max_packet= (uint32_t) buffer_length;
00073   max_packet_size= max(buffer_length, drizzled::global_system_variables.max_allowed_packet);
00074 
00075   buff= (unsigned char*) malloc((size_t) max_packet + NET_HEADER_SIZE + COMP_HEADER_SIZE);
00076   buff_end= buff + max_packet;
00077   error_= 0;
00078   pkt_nr= compress_pkt_nr= 0;
00079   write_pos= read_pos= buff;
00080   compress= 0; 
00081   where_b= remain_in_buf= 0;
00082   last_errno= 0;
00083   vio->fastsend();
00084 }
00085 
00086 void NET::end()
00087 {
00088   free(buff);
00089   buff= NULL;
00090 }
00091 
00092 void NET::close()
00093 {
00094   drizzled::safe_delete(vio);
00095 }
00096 
00097 bool NET::peer_addr(char *buf, size_t buflen, uint16_t& port)
00098 {
00099   return vio->peer_addr(buf, buflen, port);
00100 }
00101 
00102 void NET::keepalive(bool flag)
00103 {
00104   vio->keepalive(flag);
00105 }
00106 
00107 int NET::get_sd() const
00108 {
00109   return vio->get_fd();
00110 }
00111 
00114 static bool drizzleclient_net_realloc(NET *net, size_t length)
00115 {
00116   if (length >= net->max_packet_size)
00117   {
00118     /* @todo: 1 and 2 codes are identical. */
00119     net->error_= 3;
00120     net->last_errno= ER_NET_PACKET_TOO_LARGE;
00121     my_error(ER_NET_PACKET_TOO_LARGE, MYF(0));
00122     return 1;
00123   }
00124   size_t pkt_length = (length + IO_SIZE - 1) & ~(IO_SIZE - 1);
00125   /*
00126     We must allocate some extra bytes for the end 0 and to be able to
00127     read big compressed blocks
00128   */
00129   unsigned char* buff= (unsigned char*)realloc((char*) net->buff, pkt_length + NET_HEADER_SIZE + COMP_HEADER_SIZE);
00130   net->buff=net->write_pos= buff;
00131   net->buff_end= buff + (net->max_packet= (uint32_t) pkt_length);
00132   return 0;
00133 }
00134 
00135 bool NET::flush()
00136 {
00137   bool error= false;
00138   if (buff != write_pos)
00139   {
00140     error= drizzleclient_net_real_write(this, buff, write_pos - buff) ? true : false;
00141     write_pos= buff;
00142   }
00143   /* Sync packet number if using compression */
00144   if (compress)
00145     pkt_nr= compress_pkt_nr;
00146   return error;
00147 }
00148 
00149 
00150 /*****************************************************************************
00151  ** Write something to server/client buffer
00152  *****************************************************************************/
00153 
00164 static bool
00165 drizzleclient_net_write(NET* net, const void* packet0, size_t len)
00166 {
00167   const unsigned char* packet= reinterpret_cast<const unsigned char*>(packet0);
00168   unsigned char buff[NET_HEADER_SIZE];
00169   if (unlikely(!net->vio)) /* nowhere to write */
00170     return 0;
00171   /*
00172     Big packets are handled by splitting them in packets of MAX_PACKET_LENGTH
00173     length. The last packet is always a packet that is < MAX_PACKET_LENGTH.
00174     (The last packet may even have a length of 0)
00175   */
00176   while (len >= MAX_PACKET_LENGTH)
00177   {
00178     const uint32_t z_size = MAX_PACKET_LENGTH;
00179     int3store(buff, z_size);
00180     buff[3]= (unsigned char) net->pkt_nr++;
00181     if (net_write_buff(net, buff, NET_HEADER_SIZE) || net_write_buff(net, packet, z_size))
00182       return 1;
00183     packet += z_size;
00184     len-=     z_size;
00185   }
00186   /* Write last packet */
00187   int3store(buff,len);
00188   buff[3]= (unsigned char) net->pkt_nr++;
00189   return net_write_buff(net, buff, NET_HEADER_SIZE) || net_write_buff(net, packet, len);
00190 }
00191 
00219 static bool
00220 drizzleclient_net_write_command(NET *net,unsigned char command,
00221                   const unsigned char *header, size_t head_len,
00222                   const unsigned char *packet, size_t len)
00223 {
00224   uint32_t length=len+1+head_len;            /* 1 extra byte for command */
00225   unsigned char buff[NET_HEADER_SIZE+1];
00226   uint32_t header_size=NET_HEADER_SIZE+1;
00227 
00228   buff[4]=command;                /* For first packet */
00229 
00230   if (length >= MAX_PACKET_LENGTH)
00231   {
00232     /* Take into account that we have the command in the first header */
00233     len= MAX_PACKET_LENGTH - 1 - head_len;
00234     do
00235     {
00236       int3store(buff, MAX_PACKET_LENGTH);
00237       buff[3]= (unsigned char) net->pkt_nr++;
00238       if (net_write_buff(net, buff, header_size) ||
00239           net_write_buff(net, header, head_len) ||
00240           net_write_buff(net, packet, len))
00241         return(1);
00242       packet+= len;
00243       length-= MAX_PACKET_LENGTH;
00244       len= MAX_PACKET_LENGTH;
00245       head_len= 0;
00246       header_size= NET_HEADER_SIZE;
00247     } while (length >= MAX_PACKET_LENGTH);
00248     len=length;                    /* Data left to be written */
00249   }
00250   int3store(buff,length);
00251   buff[3]= (unsigned char) net->pkt_nr++;
00252   return (net_write_buff(net, buff, header_size) ||
00253           (head_len && net_write_buff(net, header, head_len)) ||
00254           net_write_buff(net, packet, len) || net->flush());
00255 }
00256 
00283 static bool
00284 net_write_buff(NET* net, const void* packet0, uint32_t len)
00285 {
00286   const unsigned char* packet= reinterpret_cast<const unsigned char*>(packet0);
00287   uint32_t left_length;
00288   if (net->compress && net->max_packet > MAX_PACKET_LENGTH)
00289     left_length= MAX_PACKET_LENGTH - (net->write_pos - net->buff);
00290   else
00291     left_length= (uint32_t) (net->buff_end - net->write_pos);
00292 
00293   if (len > left_length)
00294   {
00295     if (net->write_pos != net->buff)
00296     {
00297       /* Fill up already used packet and write it */
00298       memcpy(net->write_pos,packet,left_length);
00299       if (drizzleclient_net_real_write(net, net->buff,
00300                          (size_t) (net->write_pos - net->buff) + left_length))
00301         return 1;
00302       net->write_pos= net->buff;
00303       packet+= left_length;
00304       len-= left_length;
00305     }
00306     if (net->compress)
00307     {
00308       /*
00309         We can't have bigger packets than 16M with compression
00310         Because the uncompressed length is stored in 3 bytes
00311       */
00312       left_length= MAX_PACKET_LENGTH;
00313       while (len > left_length)
00314       {
00315         if (drizzleclient_net_real_write(net, packet, left_length))
00316           return 1;
00317         packet+= left_length;
00318         len-= left_length;
00319       }
00320     }
00321     if (len > net->max_packet)
00322       return drizzleclient_net_real_write(net, packet, len) ? 1 : 0;
00323     /* Send out rest of the blocks as full sized blocks */
00324   }
00325   memcpy(net->write_pos,packet,len);
00326   net->write_pos+= len;
00327   return 0;
00328 }
00329 
00330 
00339 /*
00340   TODO: rewrite this in a manner to do non-block writes. If a write can not be made, and we are
00341   in the server, yield to another process and come back later.
00342 */
00343 static int
00344 drizzleclient_net_real_write(NET *net, const unsigned char *packet, size_t len)
00345 {
00346   /* Backup of the original SO_RCVTIMEO timeout */
00347 
00348   if (net->error_ == 2)
00349     return(-1);                /* socket can't be used */
00350 
00351   if (net->compress)
00352   {
00353     const uint32_t header_length=NET_HEADER_SIZE+COMP_HEADER_SIZE;
00354     unsigned char* b= (unsigned char*) malloc(len + NET_HEADER_SIZE + COMP_HEADER_SIZE);
00355     memcpy(b+header_length,packet,len);
00356 
00357     size_t complen= len * 120 / 100 + 12;
00358     unsigned char* compbuf= new unsigned char[complen];
00359     uLongf tmp_complen= complen;
00360     int res= compress((Bytef*) compbuf, &tmp_complen,
00361       (Bytef*) (b+header_length),
00362       len);
00363     complen= tmp_complen;
00364 
00365     delete[] compbuf;
00366 
00367     if (res != Z_OK || complen >= len)
00368       complen= 0;
00369     else
00370     {
00371       size_t tmplen= complen;
00372       complen= len;
00373       len= tmplen;
00374     }
00375     int3store(&b[NET_HEADER_SIZE],complen);
00376     int3store(b,len);
00377     b[3]=(unsigned char) (net->compress_pkt_nr++);
00378     len+= header_length;
00379     packet= b;
00380   }
00381 
00382   uint32_t retry_count= 0;
00383   const unsigned char* pos= packet;
00384   const unsigned char* end= pos + len;
00385   /* Loop until we have read everything */
00386   while (pos != end)
00387   {
00388     assert(pos);
00389     // TODO - see bug comment below - will we crash now?
00390     size_t length;
00391     if ((long) (length= net->vio->write( pos, (size_t) (end-pos))) <= 0)
00392     {
00393      /*
00394       * We could end up here with net->vio == NULL
00395       * See LP bug#436685
00396       * If that is the case, we exit the while loop
00397       */
00398       if (net->vio == NULL)
00399         break;
00400       
00401       const bool interrupted= net->vio->should_retry();
00402       /*
00403         If we read 0, or we were interrupted this means that
00404         we need to switch to blocking mode and wait until the timeout
00405         on the socket kicks in.
00406       */
00407       if (interrupted || length == 0)
00408       {
00409         bool old_mode;
00410         while (net->vio->blocking(true, &old_mode) < 0)
00411         {
00412           if (net->vio->should_retry() && retry_count++ < net->retry_count)
00413             continue;
00414           net->error_= 2;                     /* Close socket */
00415           net->last_errno= ER_NET_PACKET_TOO_LARGE;
00416           my_error(ER_NET_PACKET_TOO_LARGE, MYF(0));
00417           goto end;
00418         }
00419         retry_count=0;
00420         continue;
00421       }
00422       else
00423       {
00424         if (retry_count++ < net->retry_count)
00425           continue;
00426       }
00427 
00428       if (net->vio->get_errno() == EINTR)
00429       {
00430         continue;
00431       }
00432       net->error_= 2;                /* Close socket */
00433       net->last_errno= interrupted ? CR_NET_WRITE_INTERRUPTED : CR_NET_ERROR_ON_WRITE;
00434       break;
00435     }
00436     pos+= length;
00437 
00438     /* If this is an error we may not have a current_session any more */
00439     if (current_session)
00440       current_session->status_var.bytes_sent+= length;
00441   }
00442 end:
00443   if (net->compress)
00444     free((char*)packet);
00445 
00446   return (int) (pos != end);
00447 }
00448 
00449 
00459 static uint32_t
00460 my_real_read(NET *net, size_t *complen)
00461 {
00462   size_t length= 0;
00463   uint32_t retry_count=0;
00464   size_t len=packet_error;
00465   uint32_t remain= net->compress ? NET_HEADER_SIZE+COMP_HEADER_SIZE : NET_HEADER_SIZE;
00466 
00467   *complen = 0;
00468 
00469   /* Read timeout is set in drizzleclient_net_set_read_timeout */
00470 
00471   unsigned char* pos = net->buff + net->where_b;        /* net->packet -4 */
00472 
00473   for (uint32_t i= 0; i < 2 ; i++)
00474   {
00475     while (remain > 0)
00476     {
00477       /* First read is done with non blocking mode */
00478       if ((long) (length= net->vio->read(pos, remain)) <= 0L)
00479       {
00480         if (net->vio == NULL)
00481           goto end;
00482 
00483         const bool interrupted = net->vio->should_retry();
00484 
00485         if (interrupted)
00486         {                    /* Probably in MIT threads */
00487           if (retry_count++ < net->retry_count)
00488             continue;
00489         }
00490         if (net->vio->get_errno() == EINTR)
00491         {
00492           continue;
00493         }
00494         len= packet_error;
00495         net->error_= 2;                /* Close socket */
00496         net->last_errno= net->vio->was_interrupted() ? CR_NET_READ_INTERRUPTED : CR_NET_READ_ERROR;
00497         goto end;
00498       }
00499       remain -= (uint32_t) length;
00500       pos+= length;
00501       current_session->status_var.bytes_received+= length;
00502     }
00503     if (i == 0)
00504     {                    /* First parts is packet length */
00505       uint32_t helping;
00506 
00507       if (net->buff[net->where_b + 3] != (unsigned char) net->pkt_nr)
00508       {
00509         len= packet_error;
00510         /* Not a NET error on the client. XXX: why? */
00511         my_error(ER_NET_PACKETS_OUT_OF_ORDER, MYF(0));
00512         goto end;
00513       }
00514       net->compress_pkt_nr= ++net->pkt_nr;
00515       if (net->compress)
00516       {
00517         /*
00518           If the packet is compressed then complen > 0 and contains the
00519           number of bytes in the uncompressed packet
00520         */
00521         *complen=uint3korr(&(net->buff[net->where_b + NET_HEADER_SIZE]));
00522       }
00523 
00524       len=uint3korr(net->buff+net->where_b);
00525       if (!len)                /* End of big multi-packet */
00526         goto end;
00527       helping = max(len,*complen) + net->where_b;
00528       /* The necessary size of net->buff */
00529       if (helping >= net->max_packet)
00530       {
00531         if (drizzleclient_net_realloc(net,helping))
00532         {
00533           /* Clear the buffer so libdrizzle doesn't keep retrying */
00534           while (len > 0)
00535           {
00536             length= read(net->vio->get_fd(), net->buff, min((size_t)net->max_packet, len));
00537             assert((long)length > 0L);
00538             len-= length;
00539           }
00540             
00541           len= packet_error;          /* Return error and close connection */
00542           goto end;
00543         }
00544       }
00545       pos=net->buff + net->where_b;
00546       remain = (uint32_t) len;
00547     }
00548   }
00549 
00550 end:
00551   return len;
00552 }
00553 
00554 
00571 static uint32_t
00572 drizzleclient_net_read(NET *net)
00573 {
00574   size_t len, complen;
00575 
00576   if (not net->compress)
00577   {
00578     len = my_real_read(net,&complen);
00579     if (len == MAX_PACKET_LENGTH)
00580     {
00581       /* First packet of a multi-packet.  Concatenate the packets */
00582       uint32_t save_pos = net->where_b;
00583       size_t total_length= 0;
00584 
00585       do
00586       {
00587         net->where_b += len;
00588         total_length += len;
00589         len = my_real_read(net,&complen);
00590       } while (len == MAX_PACKET_LENGTH);
00591 
00592       if (len != packet_error)
00593       {
00594         len+= total_length;
00595       }
00596       net->where_b = save_pos;
00597     }
00598     net->read_pos = net->buff + net->where_b;
00599 
00600     if (len != packet_error)
00601       net->read_pos[len]=0;        /* Safeguard for drizzleclient_use_result */
00602 
00603     return len;
00604   }
00605   else
00606   {
00607     /* We are using the compressed protocol */
00608 
00609     uint32_t buf_length;
00610     uint32_t start_of_packet;
00611     uint32_t first_packet_offset;
00612     uint32_t read_length, multi_byte_packet=0;
00613 
00614     if (net->remain_in_buf)
00615     {
00616       buf_length= net->buf_length;        /* Data left in old packet */
00617       first_packet_offset= start_of_packet= (net->buf_length -
00618                                              net->remain_in_buf);
00619       /* Restore the character that was overwritten by the end 0 */
00620       net->buff[start_of_packet]= net->save_char;
00621     }
00622     else
00623     {
00624       /* reuse buffer, as there is nothing in it that we need */
00625       buf_length= start_of_packet= first_packet_offset= 0;
00626     }
00627     for (;;)
00628     {
00629       uint32_t packet_len;
00630 
00631       if (buf_length - start_of_packet >= NET_HEADER_SIZE)
00632       {
00633         read_length = uint3korr(net->buff+start_of_packet);
00634         if (!read_length)
00635         {
00636           /* End of multi-byte packet */
00637           start_of_packet += NET_HEADER_SIZE;
00638           break;
00639         }
00640         if (read_length + NET_HEADER_SIZE <= buf_length - start_of_packet)
00641         {
00642           if (multi_byte_packet)
00643           {
00644             /* Remove packet header for second packet */
00645             memmove(net->buff + first_packet_offset + start_of_packet,
00646                     net->buff + first_packet_offset + start_of_packet +
00647                     NET_HEADER_SIZE,
00648                     buf_length - start_of_packet);
00649             start_of_packet += read_length;
00650             buf_length -= NET_HEADER_SIZE;
00651           }
00652           else
00653             start_of_packet+= read_length + NET_HEADER_SIZE;
00654 
00655           if (read_length != MAX_PACKET_LENGTH)    /* last package */
00656           {
00657             multi_byte_packet= 0;        /* No last zero len packet */
00658             break;
00659           }
00660           multi_byte_packet= NET_HEADER_SIZE;
00661           /* Move data down to read next data packet after current one */
00662           if (first_packet_offset)
00663           {
00664             memmove(net->buff,net->buff+first_packet_offset,
00665                     buf_length-first_packet_offset);
00666             buf_length-=first_packet_offset;
00667             start_of_packet -= first_packet_offset;
00668             first_packet_offset=0;
00669           }
00670           continue;
00671         }
00672       }
00673       /* Move data down to read next data packet after current one */
00674       if (first_packet_offset)
00675       {
00676         memmove(net->buff,net->buff+first_packet_offset,
00677                 buf_length-first_packet_offset);
00678         buf_length-=first_packet_offset;
00679         start_of_packet -= first_packet_offset;
00680         first_packet_offset=0;
00681       }
00682 
00683       net->where_b=buf_length;
00684       if ((packet_len = my_real_read(net,&complen)) == packet_error)
00685         return packet_error;
00686 
00687       if (complen)
00688       {
00689         unsigned char * compbuf= (unsigned char *) malloc(complen);
00690         if (compbuf != NULL)
00691         {
00692           uLongf tmp_complen= complen;
00693           int error= uncompress((Bytef*) compbuf, &tmp_complen,
00694                                 (Bytef*) (net->buff + net->where_b),
00695                                 (uLong)packet_len);
00696           complen= tmp_complen;
00697 
00698           if (error != Z_OK)
00699           {
00700             net->error_= 2;            /* caller will close socket */
00701             net->last_errno= CR_NET_UNCOMPRESS_ERROR;
00702           }
00703           else
00704           {
00705             memcpy((net->buff + net->where_b), compbuf, complen);
00706           }
00707           free(compbuf);
00708         }
00709       }
00710       else
00711       {
00712         complen= packet_len;
00713       }
00714 
00715     }
00716     buf_length+= complen;
00717 
00718     net->read_pos=      net->buff+ first_packet_offset + NET_HEADER_SIZE;
00719     net->buf_length=    buf_length;
00720     net->remain_in_buf= (uint32_t) (buf_length - start_of_packet);
00721     len = ((uint32_t) (start_of_packet - first_packet_offset) - NET_HEADER_SIZE -
00722            multi_byte_packet);
00723     net->save_char= net->read_pos[len];    /* Must be saved */
00724     net->read_pos[len]=0;        /* Safeguard for drizzleclient_use_result */
00725   }
00726   return len;
00727 }
00728 
00729 void NET::set_read_timeout(uint32_t timeout)
00730 {
00731   read_timeout_= timeout;
00732 #ifndef __sun
00733   if (vio)
00734     vio->timeout(0, timeout);
00735 #endif
00736   return;
00737 }
00738 
00739 void NET::set_write_timeout(uint32_t timeout)
00740 {
00741   write_timeout_= timeout;
00742 #ifndef __sun
00743   if (vio)
00744     vio->timeout(1, timeout);
00745 #endif
00746   return;
00747 }
00748 
00749 bool NET::write(const void* data, size_t size)
00750 {
00751   return drizzleclient_net_write(this, data, size);
00752 }
00753 
00754 bool NET::write_command(unsigned char command, data_ref header, data_ref body)
00755 {
00756   return drizzleclient_net_write_command(this, command, header.data(), header.size(), body.data(), body.size());
00757 }
00758 
00759 uint32_t NET::read()
00760 {
00761   return drizzleclient_net_read(this);
00762 }
00763 
00764 } /* namespace drizzle_plugin */