Main Page | Modules | File List

localkdb.c

00001 /***************************************************************************
00002             localkdb.c  -  Methods for accessing the Key Database
00003                              -------------------
00004     begin                : Mon Dec 29 2003
00005     copyright            : (C) 2003 by Avi Alkalay
00006     email                : avi@unix.sh
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  *                                                                         *
00011  *   This program is free software; you can redistribute it and/or modify  *
00012  *   it under the terms of the GNU General Public License as published by  *
00013  *   the Free Software Foundation; either version 2 of the License, or     *
00014  *   (at your option) any later version.                                   *
00015  *                                                                         *
00016  ***************************************************************************/
00017 
00018 
00019 
00020 /***************************************************************************
00021  *                                                                         *
00022  *   This is the implementation of a filesystem backend for the            *
00023  *   Elektra Project. Each Key is a file in the filesystem.                *
00024  *   It is as secure as filesystem security. It is as reliable             *
00025  *   as filesystem. It uses only standards C calls, which makes it         *
00026  *   usable by very low level or early boot stage software, like           *
00027  *   /sbin/init.                                                           *
00028  *                                                                         *
00029  ***************************************************************************/
00030 
00031 
00032 /* Subversion stuff
00033 
00034 $Id: localkdb.c 99 2004-10-25 04:11:44Z aviram $
00035 $LastChangedBy: aviram $
00036 
00037 */
00038 
00039 
00040 #include "kdb.h"
00041 #include "kdbprivate.h"
00042 
00043 
00044 #include <sys/types.h>
00045 #include <sys/stat.h>
00046 #include <fcntl.h>
00047 #include <stdlib.h>
00048 #include <unistd.h>
00049 #include <pwd.h>
00050 #include <dirent.h>
00051 #include <errno.h>
00052 #include <stdio.h>
00053 #include <iconv.h>
00054 #include <locale.h>
00055 #include <langinfo.h>
00056 #include <ctype.h>
00057 #include <string.h>
00058 #include <utmp.h>
00059 
00060 
00061 #define UTF8_TO   1
00062 #define UTF8_FROM 0
00063 
00064 #define BUFFER_SIZE 100
00065 
00066 #ifdef UT_NAMESIZE
00067 #define USER_NAME_SIZE UT_NAMESIZE
00068 #else
00069 #define USER_NAME_SIZE 100
00070 #endif
00071 
00073 #ifdef PATH_MAX
00074 #define MAX_PATH_LENGTH PATH_MAX
00075 
00076 #elif __USE_POSIX
00077 #define MAX_PATH_LENGTH _POSIX_PATH_MAX
00078 #else 
00079 #define MAX_PATH_LENGTH 4096
00080 #endif
00081 
00082 
00083 extern int errno;
00084 
00085 
00086 
00087 
00117 /*
00118  * @defgroup internals Elektra internals
00119  * @brief These methods are not to be used by your application.
00120  *
00121  */
00122 
00123 
00124 
00136 int kdbOpen() {
00137     /* load the environment and make us aware of codeset conversions */
00138     setlocale(LC_ALL,"");
00139 
00140     return 0;
00141 }
00142 
00153 int kdbClose() {
00154     return 0;
00155 }
00156 
00167 size_t keyGetSerializedSize(const Key *key) {
00168     size_t size,tmp;
00169 
00170 
00171     size=5+sizeof(u_int8_t)+1; /* RG000\nT\n */
00172     if (key->comment) size+=strblen(key->comment);
00173     size++;
00174     tmp=keyGetDataSize(key);
00175     size+=tmp;
00176     return size;
00177 }
00178 
00190 size_t unencode(char *encoded,void *returned) {
00191     char byteInHexa[5]="0x";
00192     char *readCursor=encoded;
00193     char *writeCursor=returned;
00194 
00195     if (!encoded) {
00196         if (returned) *(char *)returned=0;
00197         return 0;
00198     }
00199 
00200     byteInHexa[4]=0;
00201     while (*readCursor) {
00202         if (isspace((int)*readCursor)) 
00203         {
00204         readCursor++;
00205         continue;
00206         }
00207         if (isxdigit((int)*readCursor)) {
00208             long int converted;
00209             byteInHexa[2]=readCursor[0];
00210             byteInHexa[3]=readCursor[1];
00211             converted=strtol(byteInHexa,0,16); /* convert from hexa to a byte */
00212             *writeCursor=(unsigned char)converted;
00213 
00214             readCursor+=2;
00215             writeCursor++;
00216         } else {
00217             /* This is suposed to be a hex-digit stream. But is not, so return. */
00218             errno=KDB_RET_TYPEMISMATCH;
00219             return 0;
00220         }
00221     }
00222     return (long int)writeCursor-(long int)returned;
00223 }
00224 
00231 int kdbNeedsUTF8Conversion() {
00232     return strcmp(nl_langinfo(CODESET),"UTF-8");
00233 }
00234 
00235 
00245 int UTF8Engine(int direction, char **string, size_t *inputByteSize) {
00246     char *currentCharset=0;
00247     char *converted=0;
00248     char *readCursor, *writeCursor;
00249     size_t bufferSize;
00250     iconv_t converter;
00251 
00252     if (kdbNeedsUTF8Conversion()) currentCharset=nl_langinfo(CODESET);
00253     else return 0;
00254 
00255     if (direction==UTF8_TO) converter=iconv_open("UTF-8",currentCharset);
00256     else converter=iconv_open(currentCharset,"UTF-8");
00257 
00258     if (converter == (iconv_t)(-1)) return -1;
00259 
00260     /* work with worst case, when all chars are wide */
00261     bufferSize=*inputByteSize * 4;
00262     converted=malloc(bufferSize);
00263     if (!converted) return -1;
00264 
00265     readCursor=*string;
00266     writeCursor=converted;
00267     if (iconv(converter,
00268             &readCursor,inputByteSize,
00269             &writeCursor,&bufferSize) == (size_t)(-1)) {
00270         free(converted);
00271         iconv_close(converter);
00272         return -1;
00273     }
00274 
00275     /* calculate the UTF-8 string byte size, that will be returned */
00276     *inputByteSize=writeCursor-converted;
00277     /* store the current unencoded string for future free */
00278     readCursor=*string;
00279     /* allocate an optimal size area to store the converted string */
00280     *string=malloc(*inputByteSize);
00281     /* copy all that matters for returning */
00282     memcpy(*string,converted,*inputByteSize);
00283     /* release memory used by passed string */
00284     free(readCursor);
00285     /* release buffer memory */
00286     free(converted);
00287     /* release the conversor engine */
00288     iconv_close(converter);
00289 
00290     return 0;
00291 }
00292 
00293 
00294 
00295 
00302 int handleOldKeyFileVersion(Key *key,FILE *input,u_int16_t nversion) {
00303     char generalBuffer[BUFFER_SIZE];
00304     size_t currentBufferSize;
00305 
00306     char type[5];
00307     char *data=0;
00308     size_t dataSize=0;
00309     char *comment=0;
00310     size_t commentSize=0;
00311 
00312     int readComment=1;
00313     int eof=0;
00314 
00315     /*
00316         This is a very dirty helper.
00317         It has the parsing code for old version of key files.
00318         If your editor doesn't have code folding it will be a pain.
00319     */
00320 
00321 
00322     switch (nversion) {
00323         case 1: {
00324             if (!fgets(type,    sizeof(type),    input)) return -1;
00325 
00326             while (readComment) {
00327                 if (fgets(generalBuffer,sizeof(generalBuffer),input)) {
00328                     if (memcmp(generalBuffer,"<DATA>\n\0",8)) {
00329                         /* This is not the begining of the data part so it is part of comment */
00330                         currentBufferSize=strblen(generalBuffer);
00331                         if (!comment) {
00332                             comment=(char *)malloc(commentSize=currentBufferSize);
00333                             strcpy(comment,generalBuffer);
00334                         } else {
00335                             char *buffer=0;
00336 
00337                             buffer=malloc(commentSize+currentBufferSize);
00338                             strcpy(buffer,comment);
00339                             strcat(buffer,generalBuffer);
00340                             comment=realloc(comment,commentSize+=currentBufferSize);
00341                             strcpy(comment,buffer);
00342                             free(buffer);
00343                         }
00344                     } else readComment=0;
00345                 } else {
00346                     readComment=0;
00347                     eof=1;
00348                 }
00349             }
00350 
00351             /* Remove last \n */
00352             if (commentSize > 1 && (*(comment+commentSize-2) == '\n')) {
00353                 *(comment+commentSize-2)=0;
00354                 --commentSize;
00355             }
00356 
00357 
00358             if (comment && UTF8Engine(UTF8_FROM,&comment,&commentSize)) {
00359                 free(comment);
00360                 return -1;
00361             }
00362 
00363             /* Now read the data section */
00364             if (!eof) {
00365                 while (fgets(generalBuffer,sizeof(generalBuffer),input)) {
00366                     currentBufferSize=strlen(generalBuffer);
00367                     if (!data) {
00368                         data=(char *)malloc(dataSize=(currentBufferSize+1));
00369                         strcpy(data,generalBuffer);
00370                     } else {
00371                         char *buffer=0;
00372 
00373                         buffer=malloc(dataSize+currentBufferSize);
00374                         strcpy(buffer,data);
00375                         strcat(buffer,generalBuffer);
00376                         data=realloc(data,dataSize+=currentBufferSize);
00377                         strcpy(data,buffer);
00378                         free(buffer);
00379                     }
00380                 }
00381             }
00382 
00383             /* Put in the Key object */
00384             keySetComment(key,comment);
00385             if (comment) free(comment);
00386 
00387             /* This is what changed from version 1 to
00388                version 2 format: key type numbers */
00389             {
00390                 u_int8_t oldVersion=atoi(type);
00391                 switch (oldVersion) {
00392                     case 1: keySetType(key,KEY_TYPE_BINARY); break;
00393                     case 2: keySetType(key,KEY_TYPE_STRING); break;
00394                     default: keySetType(key,oldVersion);
00395                 }
00396             }
00397             if (!dataSize) {
00398                 keySetRaw(key,0,0);
00399                 return 0;
00400             }
00401 
00402             if (keyGetType(key) <= KEY_TYPE_BINARY) {
00403                 /* Binary data. Unencode. */
00404                 char *unencoded=0;
00405                 size_t unencodedSize;
00406 
00407                 /* raw data is maximum half the size of text-encoded data */
00408                 unencodedSize=dataSize/2;
00409 
00410                 unencoded=malloc(unencodedSize);
00411                 if (!(unencodedSize=unencode(data,unencoded))) return -1;
00412                 keySetRaw(key,unencoded,unencodedSize);
00413                 free(unencoded);
00414             } else {
00415                 if (UTF8Engine(UTF8_FROM,&data,&dataSize)) {
00416                     free(data);
00417                     return -1;
00418                 }
00419                 keySetRaw(key,data,dataSize);
00420             }
00421 
00422             free(data);
00423 
00424             return 0;
00425         } /* version 1 */
00426     } /* switch */
00427     return -1;
00428 }
00429 
00430 
00431 
00432 
00433 
00442 int keyFileUnserialize(Key *key,FILE *input) {
00443     char generalBuffer[BUFFER_SIZE];
00444     size_t currentBufferSize;
00445 
00446     char version[10];
00447     u_int16_t nversion=0;
00448     char type[5];
00449     char *data=0;
00450     size_t dataSize=0;
00451     char *comment=0;
00452     size_t commentSize=0;
00453 
00454     int readComment=1;
00455     int eof=0;
00456 
00457     /* The serialized format is
00458        -------------------------
00459        RG001\n
00460        type\n
00461        comment (with newlines)\n
00462        <DATA>\n
00463        The data encoded as text
00464        -------------------------
00465     */
00466 
00467     if (!fgets(version, sizeof(version), input)) return -1;
00468     if (strncmp(version,"RG",2)) {
00469         /* Doesn't look like a key file */
00470         errno=KDB_RET_INVALIDKEY;
00471         return -1;
00472     }
00473 
00474     nversion=atoi(version+2);
00475     if (!nversion || nversion > RG_KEY_FORMAT_VERSION) {
00476         errno=KDB_RET_INVALIDKEY;
00477         return -1;
00478     }
00479 
00480     if (nversion != RG_KEY_FORMAT_VERSION)
00481         return handleOldKeyFileVersion(key,input,nversion);
00482 
00483     if (!fgets(type,    sizeof(type),    input)) return -1;
00484 
00485     while (readComment) {
00486         if (fgets(generalBuffer,sizeof(generalBuffer),input)) {
00487             if (memcmp(generalBuffer,"<DATA>\n\0",8)) {
00488                 /* This is not the begining of the data part so it is part of comment */
00489                 currentBufferSize=strblen(generalBuffer);
00490                 if (!comment) {
00491                     comment=(char *)malloc(commentSize=currentBufferSize);
00492                     strcpy(comment,generalBuffer);
00493                 } else {
00494                     char *buffer=0;
00495 
00496                     buffer=malloc(commentSize+currentBufferSize);
00497                     strcpy(buffer,comment);
00498                     strcat(buffer,generalBuffer);
00499                     comment=realloc(comment,commentSize+=currentBufferSize);
00500                     strcpy(comment,buffer);
00501                     free(buffer);
00502                 }
00503             } else readComment=0;
00504         } else {
00505             readComment=0;
00506             eof=1;
00507         }
00508     }
00509 
00510     /* Remove last \n */
00511     if (commentSize > 1 && (*(comment+commentSize-2) == '\n')) {
00512         *(comment+commentSize-2)=0;
00513         --commentSize;
00514     }
00515 
00516     if (comment && UTF8Engine(UTF8_FROM,&comment,&commentSize)) {
00517         free(comment);
00518         return -1;
00519     }
00520 
00521     /* Now read the data section */
00522     if (!eof) {
00523         while (fgets(generalBuffer,sizeof(generalBuffer),input)) {
00524             currentBufferSize=strlen(generalBuffer);
00525             if (!data) {
00526                 data=(char *)malloc(dataSize=(currentBufferSize+1));
00527                 strcpy(data,generalBuffer);
00528             } else {
00529                 char *buffer=0;
00530 
00531                 buffer=malloc(dataSize+currentBufferSize);
00532                 strcpy(buffer,data);
00533                 strcat(buffer,generalBuffer);
00534                 data=realloc(data,dataSize+=currentBufferSize);
00535                 strcpy(data,buffer);
00536                 free(buffer);
00537             }
00538         }
00539     }
00540 
00541     /* Put in the Key object */
00542     keySetComment(key,comment);
00543     if (comment) free(comment);
00544     keySetType(key,atoi(type));
00545     if (!dataSize) {
00546         keySetRaw(key,0,0);
00547         return 0;
00548     }
00549 
00550     if (keyGetType(key) <= KEY_TYPE_BINARY) {
00551         /* Binary data. Unencode. */
00552         char *unencoded=0;
00553         size_t unencodedSize;
00554 
00555         /* raw data is maximum half the size of text-encoded data */
00556         unencodedSize=dataSize/2;
00557 
00558         unencoded=malloc(unencodedSize);
00559         if (!(unencodedSize=unencode(data,unencoded))) return -1;
00560         keySetRaw(key,unencoded,unencodedSize);
00561         free(unencoded);
00562     } else {
00563         if (UTF8Engine(UTF8_FROM,&data,&dataSize)) {
00564             free(data);
00565             return -1;
00566         }
00567         keySetRaw(key,data,dataSize);
00568     }
00569 
00570     free(data);
00571 
00572     return 0;
00573 }
00574 
00575 
00576 
00591 size_t encode(void *unencoded, size_t size, char *returned) {
00592     void *readCursor=unencoded;
00593     void *writeCursor=returned;
00594     int blockStep=4; /* 4 bytes per block */
00595     int lineStep=8*blockStep; /* 8 blocks per line */
00596     int currentInBlock=0;
00597     int currentInLine=0;
00598 
00599     while ((readCursor-unencoded)<size) {
00600         sprintf(writeCursor,"%02x",*(u_int8_t *)readCursor);
00601         readCursor++;
00602         writeCursor+=2;
00603         currentInBlock++;
00604         currentInLine++;
00605         if (currentInLine==lineStep) {
00606             *(char *)writeCursor='\n'; writeCursor++;
00607             currentInLine=0;
00608             currentInBlock=0;
00609         }
00610         if (currentInBlock==blockStep) {
00611             *(char *)writeCursor=' '; writeCursor++;
00612             currentInBlock=0;
00613         }
00614     }
00615     *(char *)writeCursor='\n';
00616     *(char *)++writeCursor=0;
00617     return (writeCursor)-(void *)returned;
00618 }
00619 
00620 
00621 
00622 
00623 
00634 int keyFileSerialize(Key *key, FILE *output) {
00635     /* The serialized format is
00636        -------------------------
00637        RG001\n
00638        type\n
00639        comment (with newlines)\n
00640        <DATA>\n
00641        The data encoded as text
00642        -------------------------
00643     */
00644 
00645     size_t dataSize;
00646 
00647     fprintf(output,"RG%03d\n",RG_KEY_FORMAT_VERSION);
00648     fprintf(output,"%d\n",key->type);
00649     if (key->comment) {
00650         if (kdbNeedsUTF8Conversion()) {
00651             size_t convertedCommentSize=key->commentSize;
00652             char *convertedComment=malloc(convertedCommentSize);
00653 
00654             memcpy(convertedComment,key->comment,key->commentSize);
00655             if (UTF8Engine(UTF8_TO,&convertedComment,&convertedCommentSize)) {
00656                 free(convertedComment);
00657                 return -1;
00658             }
00659             fprintf(output,"%s\n",convertedComment);
00660             free(convertedComment);
00661         } else fprintf(output,"%s\n",key->comment);
00662     }
00663 
00664     fputs("<DATA>\n",output);
00665     fflush(output);
00666 
00667     dataSize=keyGetDataSize(key);
00668     if (dataSize) {
00669         /* There is some data to write */
00670         if (keyGetType(key) > KEY_TYPE_BINARY) {
00671             if (kdbNeedsUTF8Conversion()) {
00672                 size_t convertedDataSize=key->dataSize;
00673                 char *convertedData=malloc(convertedDataSize);
00674 
00675                 memcpy(convertedData,key->data,key->dataSize);
00676                 if (UTF8Engine(UTF8_TO,&convertedData,&convertedDataSize)) {
00677                     free(convertedData);
00678                     return -1;
00679                 }
00680                 fprintf(output,"%s",convertedData);
00681                 free(convertedData);
00682             } else fputs(key->data,output);
00683         } else {
00684             char *encoded=malloc(3*dataSize);
00685             size_t encodedSize;
00686 
00687             encodedSize=encode(key->data,dataSize,encoded);
00688             fwrite(encoded,encodedSize,1,output);
00689             free(encoded);
00690         }
00691     }
00692     return 0;
00693 }
00694 
00695 
00705 size_t keyCalcRelativeFileName(Key *key,char *relativeFileName,size_t maxSize) {
00706     if (!key || !keyIsInitialized(key)) {
00707         errno=KDB_RET_UNINITIALIZED;
00708         return 0;
00709     }
00710     if (!key->key) {
00711         errno=KDB_RET_NOKEY;
00712         return 0;
00713     }
00714 
00715 //  cursor=key->key;
00716 //  while (*cursor) {
00717 //      if (pos+1 > maxSize) {
00718 //          errno=E2BIG;
00719 //          return -1;
00720 //      }
00721 //      switch (*cursor) {
00722 //          case '\\':
00723 //              cursor++;
00724 //              relativeFileName[pos]=*cursor;
00725 //              break;
00726 //          case '.':
00727 //              relativeFileName[pos]='/';
00728 //              break;
00729 //          default:
00730 //              relativeFileName[pos]=*cursor;
00731 //      }
00732 //      cursor++;
00733 //      pos++;
00734 //  }
00735 //  relativeFileName[pos]=0;
00736 //  pos++;
00737 
00738     if (kdbNeedsUTF8Conversion()) {
00739         char *converted;
00740         size_t size;
00741 
00742         if (!(size=keyGetNameSize(key))) return 0;
00743 
00744         converted=malloc(size);
00745         keyGetName(key,converted,size);
00746 
00747 //      memcpy(converted,relativeFileName,convertedSize);
00748 
00749         if (UTF8Engine(UTF8_TO,&converted,&size)) {
00750             free(converted);
00751             return 0;
00752         }
00753 
00754         if (size>maxSize) {
00755             free(converted);
00756             errno=E2BIG;
00757             return 0;
00758         }
00759 
00760         memcpy(relativeFileName,converted,size);
00761         free(converted);
00762 
00763         return size;
00764     } else return keyGetName(key,relativeFileName,maxSize);
00765 
00766     return 0;
00767 }
00768 
00769 
00770 
00771 
00772 
00773 
00785 int keyFromStat(Key *key,struct stat *stat) {
00786     if (!key) {
00787         errno=KDB_RET_NULLKEY;
00788         return -1;
00789     }
00790 
00791     keySetAccess(key,stat->st_mode);
00792     keySetUID(key,stat->st_uid);
00793     keySetGID(key,stat->st_gid);
00794     if (S_ISDIR(stat->st_mode)) keySetType(key,KEY_TYPE_DIR);
00795     key->atime=stat->st_atime;
00796     key->mtime=stat->st_mtime;
00797     key->ctime=stat->st_ctime;
00798     key->recordSize=stat->st_size;
00799     return 0;
00800 }
00801 
00802 
00803 
00804 
00814 size_t kdbGetFilename(Key *forKey,char *returned,size_t maxSize) {
00815     size_t length=0;
00816 
00817     switch (keyGetNamespace(forKey)) {
00818         case KEY_NS_SYSTEM: {
00819             /* Prepare to use the 'system/ *' database */
00820             strncpy(returned,RG_DB_SYSTEM,maxSize);
00821             length=strlen(returned);
00822             break;
00823         }
00824         case KEY_NS_USER: {
00825             /* Prepare to use the 'user:????/ *' database */
00826             struct passwd *user=0;
00827             char userName[USER_NAME_SIZE];
00828 
00829             length=keyGetOwner(forKey,userName,sizeof(userName));
00830             if (!length) strncpy(userName,getenv("USER"),sizeof(userName));
00831 
00832             user=getpwnam(userName);
00833                         if (!user) return 0; /* propagate errno */
00834             length=snprintf(returned,maxSize,"%s/%s",user->pw_dir,RG_DB_USER);
00835             break;
00836         }
00837         default: {
00838             errno=KDB_RET_INVALIDKEY;
00839             return 0;
00840         }
00841     }
00842 
00843     returned[length]='/'; length++;
00844     length+=keyCalcRelativeFileName(forKey,returned+length,maxSize-length);
00845 
00846     return length;
00847 }
00848 
00849 
00850 
00867 int kdbGetValue(const char *keyname, char *returned,size_t maxSize) {
00868     Key key;
00869     int rc=0;
00870 
00871     keyInit(&key);
00872     keySetName(&key,keyname);
00873     rc=kdbGetKey(&key);
00874     if (rc == 0) keyGetString(&key,returned,maxSize);
00875     else rc=errno; /* store errno before a possible change */
00876     keyClose(&key);
00877     errno=rc;
00878     return rc;
00879 }
00880 
00881 
00882 
00898 int kdbSetValue(const char *keyname, const char *value) {
00899     Key key;
00900     int rc;
00901 
00902 /* TODO: check key type first */
00903     keySetName(&key,keyname);
00904     rc=kdbGetKey(&key);
00905     keySetString(&key,value);
00906     rc=kdbSetKey(&key);
00907     keyClose(&key);
00908     return rc;
00909 }
00910 
00911 
00912 
00939 int kdbGetValueByParent(const char *parentName, const char *baseName, char *returned, size_t maxSize) {
00940     char name[strblen(parentName)+strblen(baseName)];
00941 
00942     sprintf(name,"%s/%s",parentName,baseName);
00943     return kdbGetValue(name,returned,maxSize);
00944 }
00945 
00946 
00947 
00957 int kdbSetValueByParent(const char *parentName, const char *baseName, const char *value) {
00958     char name[strblen(parentName)+strblen(baseName)];
00959 
00960     sprintf(name,"%s/%s",parentName,baseName);
00961     return kdbSetValue(name,value);
00962 }
00963 
00964 
00965 
00982 int kdbGetKeyByParent(const char *parentName, const char *baseName, Key *returned) {
00983     char name[strblen(parentName)+strblen(baseName)];
00984 
00985     sprintf(name,"%s/%s",parentName,baseName);
00986     keySetName(returned,name);
00987 
00988     return kdbGetKey(returned);
00989 }
00990 
00991 
01001 int kdbGetKeyByParentKey(const Key *parent, const char *baseName, Key *returned) {
01002     size_t size=keyGetFullNameSize(parent);
01003     char name[size+strblen(baseName)];
01004 
01005     keyGetFullName(parent,name,size);
01006     name[size-1]='/';
01007     strcpy((char *)(name+size),baseName);
01008 
01009     keySetName(returned,name);
01010 
01011     return kdbGetKey(returned);
01012 }
01013 
01014 
01015 
01016 
01017 /* Used by the qsort() function */
01018 int keyCompareByName(const void *p1, const void *p2) {
01019     Key *key1=*(Key **)p1;
01020     Key *key2=*(Key **)p2;
01021 
01022     return strcmp(key1->key, key2->key);
01023 }
01024 
01025 
01026 
01100 int kdbGetChildKeys(const char *parentName, KeySet *returned, unsigned long options) {
01101     char *realParentName=0;
01102     size_t parentNameSize;
01103     DIR *parentDir;
01104     Key parentKey;
01105     char buffer[MAX_PATH_LENGTH];
01106     struct dirent *entry;
01107 
01108     /*
01109         - Convert parent key name into a real filename
01110         - Check if it is a directory. Open it
01111         - Browse, read and include in the KeySet
01112     */
01113     keyInit(&parentKey);
01114     keySetName(&parentKey,parentName);
01115     kdbGetFilename(&parentKey,buffer,sizeof(buffer));
01116     parentDir=opendir(buffer);
01117 
01118     /* Check if Key is not a directory or doesn't exist.
01119      * Propagate errno */
01120     if (!parentDir) return -1;
01121 
01122     parentNameSize=keyGetFullNameSize(&parentKey);
01123     realParentName=realloc(realParentName,parentNameSize);
01124     keyGetFullName(&parentKey,realParentName,parentNameSize);
01125 
01126     while ((entry=readdir(parentDir))) {
01127         Key *keyEntry;
01128         char *transformedName=0;
01129         size_t keyNameSize=0;
01130 
01131         /* Ignore '.' and '..' directory entries */
01132         if (!strcmp(entry->d_name,".") || !strcmp(entry->d_name,"..")) continue;
01133 
01134         /* If key name starts with '.', and don't want INACTIVE keys, ignore it */
01135         if ((*entry->d_name == '.') && !(options & KDB_O_INACTIVE)) continue;
01136 
01137         /* Next 2 ifs are required to transform filename from UTF-8 */
01138         if (!transformedName) {
01139             transformedName=
01140                 realloc(transformedName,keyNameSize=strblen(entry->d_name));
01141             strcpy(transformedName,entry->d_name);
01142         }
01143         if (UTF8Engine(UTF8_FROM,&transformedName,&keyNameSize)) {
01144             free(transformedName);
01145             return -1;
01146         }
01147 
01148         /* Copy the entire transformed key name to our final buffer */
01149         sprintf(buffer,"%s/%s",realParentName,transformedName);
01150         free(transformedName); /* don't need it anymore */
01151 
01152         keyEntry=(Key *)malloc(sizeof(Key)); keyInit(keyEntry);
01153         keySetName(keyEntry,buffer);
01154 
01155         if (options & KDB_O_STATONLY) kdbStatKey(keyEntry);
01156         else if (options & KDB_O_NFOLLOWLINK) {
01157             kdbStatKey(keyEntry);
01158             if (!keyIsLink(keyEntry)) kdbGetKey(keyEntry);
01159         } else {
01160             int rc=kdbGetKey(keyEntry);
01161             /* If this is a permission problem, at least stat the key */
01162             if (rc && errno==KDB_RET_NOCRED) kdbStatKey(keyEntry);
01163         }
01164 
01165         if (S_ISDIR(keyEntry->access)) {
01166             if (options & KDB_O_RECURSIVE) {
01167                 KeySet children;
01168                 char *fullName;
01169                 size_t fullNameSize;
01170 
01171                 fullName=malloc(fullNameSize=keyGetFullNameSize(keyEntry));
01172                 keyGetFullName(keyEntry,fullName,fullNameSize);
01173 
01174                 ksInit(&children);
01175                 /* Act recursively, without sorting. Sort in the end, once */
01176                 kdbGetChildKeys(fullName,&children, ~(KDB_O_SORT) & options);
01177 
01178                 /* Insert the current directory key in the returned list before its children */
01179                 if (options & KDB_O_DIR) ksAppend(returned,keyEntry);
01180                 else {
01181                     keyClose(keyEntry);
01182                     free(keyEntry);
01183                 }
01184 
01185                 /* Insert the children */
01186                 ksAppendKeys(returned,&children);
01187                 free(fullName);
01188             } else if (options & KDB_O_DIR) ksAppend(returned,keyEntry);
01189                 else {
01190                     keyClose(keyEntry);
01191                     free(keyEntry);
01192                 }
01193         } else if (options & KDB_O_NOVALUE) {
01194             keyClose(keyEntry);
01195             free(keyEntry);
01196         } else ksAppend(returned,keyEntry);
01197     } /* while(readdir) */
01198     keyClose(&parentKey);
01199     free(realParentName);
01200 
01201     if ((options & (KDB_O_SORT)) && (returned->size > 1)) {
01202         Key *keys[returned->size];
01203         Key *cursor;
01204         size_t c=0;
01205 
01206         for (cursor=returned->start; cursor; cursor=cursor->next, c++)
01207             keys[c]=cursor;
01208 
01209         qsort(keys,returned->size,sizeof(Key *),keyCompareByName);
01210 
01211         returned->start=cursor=keys[0];
01212         for (c=1; c<returned->size; c++) {
01213             cursor->next=keys[c];
01214             cursor=cursor->next;
01215         }
01216         cursor->next=0;
01217         returned->end=cursor;
01218     }
01219 
01220     return 0;
01221 }
01222 
01223 
01224 
01225 
01226 
01227 
01228 
01229 
01240 int kdbGetRootKeys(KeySet *returned) {
01241     Key *system=0,*user=0;
01242 
01243     system=malloc(sizeof(Key));
01244     keyInit(system);
01245     keySetName(system,"system");
01246     if (kdbGetKey(system)) {
01247         free(system);
01248         system=0;
01249     }
01250 
01251     user=malloc(sizeof(Key));
01252     keyInit(user);
01253     keySetName(user,"user");
01254     if (kdbGetKey(user)) {
01255         free(user);
01256         user=0;
01257     }
01258 
01259     if (system) ksInsert(returned,system);
01260     if (user) ksAppend(returned,user);
01261 
01262     return 0;
01263 }
01264 
01265 
01266 
01278 int kdbStatKey(Key *key) {
01279     char keyFileName[MAX_PATH_LENGTH];
01280     struct stat keyFileNameInfo;
01281     size_t pos;
01282     u_int32_t semiflag;
01283 
01284     pos=kdbGetFilename(key,keyFileName,sizeof(keyFileName));
01285     if (!pos) return -1; /* something is wrong */
01286 
01287     if (lstat(keyFileName,&keyFileNameInfo)) return -1;
01288     keyFromStat(key,&keyFileNameInfo);
01289 
01290     if (keyIsLink(key) && key->recordSize) {
01291         char *data=malloc(key->recordSize+1); /* Add 1 byte for ending 0 */
01292 
01293         readlink(keyFileName,data,key->recordSize);
01294         data[key->recordSize]=0; /* null terminate it */
01295         keySetLink(key,data);
01296         free(data);
01297     }
01298 
01299     /* Remove the NEEDSYNC flag */
01300     semiflag=KEY_FLAG_NEEDSYNC;
01301     semiflag=~semiflag;
01302     key->flags &= semiflag;
01303     key->flags |= KEY_FLAG_ACTIVE;
01304 
01305     return 0;
01306 }
01307 
01308 
01309 
01318 int kdbGetKey(Key *key) {
01319     char keyFileName[500];
01320     struct stat keyFileNameInfo;
01321     int fd;
01322     size_t pos;
01323     u_int32_t semiflag;
01324 
01325     pos=kdbGetFilename(key,keyFileName,sizeof(keyFileName));
01326     if (!pos) return -1; /* something is wrong */
01327 
01328     if ((fd=open(keyFileName,O_RDONLY))==-1) return -1;
01329     /* TODO: lock at this point */
01330     fstat(fd,&keyFileNameInfo);
01331     keyFromStat(key,&keyFileNameInfo);
01332     if (!keyIsDir(key)) {
01333         FILE *input;
01334 
01335         input=fdopen(fd,"r");
01336         if (keyFileUnserialize(key,input)) {
01337             fclose(input);
01338             return -1;
01339         }
01340         /* TODO: unlock at this point */
01341         fclose(input);
01342     } else close(fd);
01343 
01344     /* Remove the NEEDSYNC flag */
01345     semiflag=KEY_FLAG_NEEDSYNC;
01346     semiflag=~semiflag;
01347     key->flags &= semiflag;
01348 
01349     return 0;
01350 }
01351 
01352 
01353 
01376 int kdbSetKeys(KeySet *ks) {
01377     Key *current=ksCurrent(ks);
01378     int ret;
01379 
01380     if (!current) current=ksNext(ks);
01381     while (current) {
01382         if (keyNeedsSync(current))
01383             if ((ret=kdbSetKey(current))) /* check error */
01384                 return ret;
01385         
01386         current=ksNext(ks);
01387     }
01388 
01389     return 0;
01390 }
01391 
01392 
01393 
01404 int kdbSetKey(Key *key) {
01405     char keyFileName[MAX_PATH_LENGTH];
01406     char folderMaker[MAX_PATH_LENGTH];
01407     char *cursor, *last;
01408     int fd;
01409     FILE *output=0;
01410     size_t pos;
01411     u_int32_t semiflag;
01412     struct stat stated;
01413 
01414     pos=kdbGetFilename(key,keyFileName,sizeof(keyFileName));
01415     if (!pos) return -1; /* Something is wrong. Propagate errno. */
01416 
01417     if (stat(keyFileName,&stated))
01418         if (errno==ENOENT) {
01419             /* check if parent dir already exists */
01420             last=rindex(keyFileName,'/');
01421             strncpy(folderMaker,keyFileName,last-keyFileName);
01422             folderMaker[last-keyFileName]=0;
01423             if (stat(folderMaker,&stated)) {
01424                 /* create all path recursively until before our basename */
01425                 last   =rindex(keyFileName,'/');
01426                 cursor = index(keyFileName,'/'); cursor++; /* skip first occurence */
01427                 if (!last || !cursor) { /* bizarre key name */
01428                     errno=KDB_RET_INVALIDKEY;
01429                     return -1;
01430                 }
01431                 for (cursor=index(cursor,'/');
01432                         cursor && (cursor <= last);
01433                         cursor=index(cursor,'/')) {
01434                     strncpy(folderMaker,keyFileName,cursor-keyFileName);
01435                     folderMaker[cursor-keyFileName]=0;
01436                     if (mkdir(folderMaker,0775)<0 && errno!=EEXIST) return -1;
01437                     cursor++;
01438                 }
01439             }
01440         } else return -1; /* propagate errno */
01441     else { /* A file or dir or link is already there. Lets check details */
01442         /* TODO: Check for existing link */
01443         if ( S_ISDIR(stated.st_mode) && !keyIsDir(key)) {
01444             errno=EISDIR;
01445             return -1;
01446         }
01447         if (!S_ISDIR(stated.st_mode) &&  keyIsDir(key)) {
01448             errno=ENOTDIR;
01449             return -1;
01450         }
01451     }
01452 
01453     /* Enough of checking. Now real write stuff, with a bit other checks :-) */
01454 
01455     if (keyIsLink(key)) {
01456         char *targetName=0;
01457         Key target;
01458         int rc;
01459 
01460         /*
01461             If targetName starts with:
01462             - "system" | "user" | any future root name: Convert to a FS path,
01463               and symlink it
01464             - other: It is an absolute FS path, or relative inside-kdb
01465               namespace path, and symlink it
01466         */
01467 
01468         keyInit(&target);
01469         if (key->data) targetName=malloc(key->dataSize);
01470         keyGetLink(key,targetName,key->dataSize);
01471 
01472         /* Setting the name will let us know if this is a valid keyname */
01473         if (keySetName(&target,targetName)) {
01474             /* target has a valid key name */
01475             targetName=realloc(targetName,MAX_PATH_LENGTH);
01476             kdbGetFilename(&target,targetName,MAX_PATH_LENGTH);
01477             keyClose(&target);
01478         } else if (errno==KDB_RET_INVALIDKEY) {
01479             /* Is an invalid key name. So treat it as a regular file */
01480             keyClose(&target); /* get rid of invalid stuff */
01481         } else {
01482             keyClose(&target); /* get rid of invalid stuff */
01483             return -1; /* propagate errno from keySetName() */
01484         }
01485 
01486 
01487         /* Now, targetName has the real destination of our link */
01488 
01489         /* TODO: handle null targetName */
01490         rc=symlink(targetName,keyFileName);
01491         free(targetName);
01492 
01493         return rc; /* propagate errno */
01494     } else if (keyIsDir(key)) {
01495         if (mkdir(keyFileName,keyGetAccess(key))<0 &&
01496                 errno!=EEXIST) return -1;
01497     } else {
01498         /* Try to open key file with its full file name */
01499         /* TODO: Make it more "transactional" without truncating */
01500         fd=open(keyFileName,O_CREAT | O_RDWR | O_TRUNC, keyGetAccess(key));
01501         /* TODO: lock file here */
01502         if (fd==-1) return -1;
01503         if (getuid() == 0) fchown(fd,keyGetUID(key),keyGetGID(key));
01504         if (!(output=fdopen(fd,"w+"))) return -1;
01505         if (keyFileSerialize(key,output)) {
01506             fclose(output);
01507             return -1;
01508         }
01509         /* TODO: unlock file here */
01510         fclose(output);
01511     }
01512 
01513     /* Remove the NEEDSYNC flag */
01514     semiflag=KEY_FLAG_NEEDSYNC;
01515     semiflag=~semiflag;
01516     key->flags &= semiflag;
01517 
01518     return 0;
01519 }
01520 
01521 
01522 
01523 
01532 int kdbRename(Key *key, const char *newName) {
01533     char oldFileName[MAX_PATH_LENGTH];
01534     char newFileName[MAX_PATH_LENGTH];
01535     Key newKey;
01536     int rc;
01537     
01538     keyInit(&newKey);
01539     rc=keySetName(&newKey,newName);
01540     if (rc) return rc;
01541     rc=kdbGetFilename(key,oldFileName,sizeof(oldFileName));
01542     if (!rc) return -1;
01543     rc=kdbGetFilename(&newKey,newFileName,sizeof(newFileName));
01544     if (!rc) return -1;
01545     
01546     return rename(oldFileName,newFileName);
01547 }
01548 
01549 
01550 
01551 
01552 
01562 int kdbRemove(const char *keyName) {
01563     Key key;
01564     char fileName[MAX_PATH_LENGTH];
01565     off_t rc;
01566 
01567     keyInit(&key);
01568     rc=keySetName(&key,keyName);
01569     if (rc==-1) return -1;
01570     rc=kdbGetFilename(&key,fileName,sizeof(fileName));
01571     keyClose(&key);
01572     if (!rc) return -1;
01573 
01574     return remove(fileName);
01575 }
01576 
01577 
01578 
01579 
01580 
01592 int kdbLink(const char *oldPath, const char *newKeyName) {
01593     Key key;
01594     int rc;
01595 
01596     keyInit(&key);
01597     keySetName(&key,newKeyName);
01598     keySetLink(&key,oldPath);
01599 
01600     rc=kdbSetKey(&key);
01601     keyClose(&key);
01602 
01603     return rc;
01604 }
01605 
01606 
01607 
01608 
01669 u_int32_t kdbMonitorKeys(KeySet *interests, u_int32_t diffMask,
01670         unsigned long iterations, unsigned sleep) {
01671     Key *start,*current;
01672     u_int32_t diff;
01673     int infinitum=0;
01674 
01675     if (!interests || !interests->size) return 0;
01676 
01677     /* Unacceptable 0 usecs sleep. Defaults to 1 second */
01678     if (!sleep) sleep=1000;
01679 
01680     if (!iterations) infinitum=1;
01681     else infinitum=0;
01682 
01683     current=start=ksCurrent(interests);
01684 
01685     while (infinitum || --iterations) {
01686         do {
01687             diff=kdbMonitorKey(current,diffMask,1,0);
01688             if (diff) return diff;
01689             current=ksNext(interests);
01690         } while (current!=start);
01691 
01692         /* Test if some iterations left . . . */
01693         if (infinitum || iterations) usleep(sleep);
01694     }
01695     return 0;
01696 }
01697 
01698 
01699 
01739 u_int32_t kdbMonitorKey(Key *interest, u_int32_t diffMask,
01740         unsigned long iterations, unsigned sleep) {
01741     Key tested;
01742     int rc;
01743     u_int32_t diff;
01744     int infinitum=0;
01745 
01746     /* consistency */
01747     if (!interest || !keyGetNameSize(interest)) return 0;
01748 
01749     /* Unacceptable 0 usecs sleep. Defaults to 1 second */
01750     if (!sleep) sleep=1000;
01751 
01752     if (!iterations) infinitum=1;
01753     else infinitum=0;
01754 
01755     /* Work with a copy of the key */
01756     keyInit(&tested);
01757     keyDup(interest,&tested);
01758 
01759     while (infinitum || --iterations) {
01760         rc=kdbGetKey(&tested);
01761         if (rc) {
01762             /* check what type of problem happened.... */
01763             switch (errno) {
01764                 case KDB_RET_NOCRED:
01765                     return KEY_FLAG_NEEDSYNC;
01766                 case KDB_RET_NOTFOUND:
01767                     return KEY_FLAG_FLAG;
01768             }
01769         }
01770         diff=keyCompare(&tested,interest);
01771         if (diff & diffMask) {
01772             /* If differences are in the diff mask...*/
01773             keyDup(&tested,interest);
01774             keyClose(&tested);
01775             return diff;
01776         }
01777         /* Test if some iterations left . . . */
01778         if (infinitum || iterations) usleep(sleep);
01779     }
01780 
01781     return 0;
01782 }
01783 
01784 
01785 

Generated on Sun May 8 02:29:55 2005 for Elektra Project by  doxygen 1.3.9.1