added "hide files" option.
[kai/samba.git] / source3 / lib / util.c
index 2f3ac1bb150685c306f53bd6b6dc8ddd47444bbc..889aa0b976baf1b664cb4073f8976c25877d7cb7 100644 (file)
@@ -2,7 +2,7 @@
    Unix SMB/Netbios implementation.
    Version 1.9.
    Samba utility functions
-   Copyright (C) Andrew Tridgell 1992-1995
+   Copyright (C) Andrew Tridgell 1992-1997
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -38,10 +38,6 @@ FILE *dbf = NULL;
 /* the client file descriptor */
 int Client = -1;
 
-/* info on the client */
-struct from_host Client_info=
-{"UNKNOWN","0.0.0.0",NULL};
-
 /* the last IP received from */
 struct in_addr lastip;
 
@@ -58,7 +54,7 @@ int trans_num = 0;
 */
 int case_default = CASE_LOWER;
 
-pstring debugf = "/tmp/log.samba";
+pstring debugf = "";
 int syslog_level;
 
 /* the following control case operations - they are put here so the
@@ -77,6 +73,7 @@ pstring myhostname="";
 pstring user_socket_options="";   
 pstring sesssetup_user="";
 pstring myname = "";
+fstring myworkgroup = "";
 
 int smb_read_error = 0;
 
@@ -123,6 +120,7 @@ void reopen_logs(void)
 
       if (!strcsequal(fname,debugf) || !dbf || !file_exist(debugf,NULL))
        {
+         int oldumask = umask(022);
          strcpy(debugf,fname);
          if (dbf) fclose(dbf);
          if (append_log)
@@ -130,6 +128,7 @@ void reopen_logs(void)
          else
            dbf = fopen(debugf,"w");
          if (dbf) setbuf(dbf,NULL);
+         umask(oldumask);
        }
     }
   else
@@ -205,7 +204,9 @@ va_dcl
     {
       if (!dbf) 
        {
+         int oldumask = umask(022);
          dbf = fopen(debugf,"w");
+         umask(oldumask);
          if (dbf)
            setbuf(dbf,NULL);
          else
@@ -270,6 +271,21 @@ va_dcl
   return(0);
 }
 
+/****************************************************************************
+  find a suitable temporary directory. The result should be copied immediately
+  as it may be overwritten by a subsequent call
+  ****************************************************************************/
+char *tmpdir(void)
+{
+  char *p;
+  if ((p = getenv("TMPDIR"))) {
+    return p;
+  }
+  return "/tmp";
+}
+
+
+
 /****************************************************************************
 determine if a file descriptor is in fact a socket
 ****************************************************************************/
@@ -477,6 +493,12 @@ struct
 #endif
 #ifdef SO_RCVLOWAT
   {"SO_RCVLOWAT",       SOL_SOCKET,    SO_RCVLOWAT,     0,                 OPT_INT},
+#endif
+#ifdef SO_SNDTIMEO
+  {"SO_SNDTIMEO",       SOL_SOCKET,    SO_SNDTIMEO,     0,                 OPT_INT},
+#endif
+#ifdef SO_RCVTIMEO
+  {"SO_RCVTIMEO",       SOL_SOCKET,    SO_RCVTIMEO,     0,                 OPT_INT},
 #endif
   {NULL,0,0,0,0}};
 
@@ -592,7 +614,7 @@ char *StrCpy(char *dest,char *src)
 /****************************************************************************
 line strncpy but always null terminates. Make sure there is room!
 ****************************************************************************/
-char *StrnCpy(char *dest,const char *src,int n)
+char *StrnCpy(char *dest,char *src,int n)
 {
   char *d = dest;
   if (!dest) return(NULL);
@@ -775,25 +797,32 @@ char *attrib_string(int mode)
 /*******************************************************************
   case insensitive string compararison
 ********************************************************************/
-int StrCaseCmp(char *s, char *t)
+int StrCaseCmp(const char *s, const char *t)
 {
-  for (; tolower(*s) == tolower(*t); ++s, ++t)
-    if (!*s) return 0;
+  /* compare until we run out of string, either t or s, or find a difference */
+  while (*s && *t && tolower(*s) == tolower(*t))
+  {
+    s++; t++;
+  }
 
-  return tolower(*s) - tolower(*t);
+  return(tolower(*s) - tolower(*t));
 }
 
 /*******************************************************************
   case insensitive string compararison, length limited
 ********************************************************************/
-int StrnCaseCmp(char *s, char *t, int n)
+int StrnCaseCmp(const char *s, const char *t, int n)
 {
-  while (n-- && *s && *t) {
-    if (tolower(*s) != tolower(*t)) return(tolower(*s) - tolower(*t));
+  /* compare until we run out of string, either t or s, or chars */
+  while (n-- && *s && *t && tolower(*s) == tolower(*t))
+  {
     s++; t++;
   }
+
+  /* not run out of chars - strings are different lengths */
   if (n) return(tolower(*s) - tolower(*t));
 
+  /* identical up to where we run out of chars, and strings are same length */
   return(0);
 }
 
@@ -936,9 +965,6 @@ void unix_format(char *fname)
 {
   pstring namecopy;
   string_replace(fname,'\\','/');
-#ifndef KANJI
-  dos2unix_format(fname, True);
-#endif /* KANJI */
 
   if (*fname == '/')
     {
@@ -953,9 +979,6 @@ void unix_format(char *fname)
 ****************************************************************************/
 void dos_format(char *fname)
 {
-#ifndef KANJI
-  unix2dos_format(fname, True);
-#endif /* KANJI */
   string_replace(fname,'/','\\');
 }
 
@@ -1162,6 +1185,13 @@ void unix_clean_name(char *s)
   /* remove any double slashes */
   string_sub(s, "//","/");
 
+  /* Remove leading ./ characters */
+  if(strncmp(s, "./", 2) == 0) {
+    trim_string(s, "./", NULL);
+    if(*s == 0)
+      strcpy(s,"./");
+  }
+
   while ((p = strstr(s,"/../")) != NULL)
     {
       pstring s1;
@@ -1356,6 +1386,10 @@ BOOL reduce_name(char *s,char *dir,BOOL widelinks)
          DEBUG(3,("Illegal file name? (%s)\n",s));
          return(False);
        }
+
+      if (strlen(s) == 0)
+        strcpy(s,"./");
+
       return(True);
     }
   
@@ -1655,6 +1689,35 @@ void close_low_fds(void)
   }
 }
 
+/****************************************************************************
+Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available,
+else
+if SYSV use O_NDELAY
+if BSD use FNDELAY
+****************************************************************************/
+int set_blocking(int fd, int set)
+{
+  int val;
+#ifdef O_NONBLOCK
+#define FLAG_TO_SET O_NONBLOCK
+#else
+#ifdef SYSV
+#define FLAG_TO_SET O_NDELAY
+#else /* BSD */
+#define FLAG_TO_SET FNDELAY
+#endif
+#endif
+
+  if((val = fcntl(fd, F_GETFL, 0)) == -1)
+       return -1;
+  if(set) /* Turn blocking on - ie. clear nonblock flag */
+       val &= ~FLAG_TO_SET;
+  else
+    val |= FLAG_TO_SET;
+  return fcntl( fd, F_SETFL, val);
+#undef FLAG_TO_SET
+}
+
 
 /****************************************************************************
 write to a socket
@@ -2813,10 +2876,12 @@ int open_socket_in(int type, int port, int dlevel,uint32 socket_addr)
 /****************************************************************************
   create an outgoing socket
   **************************************************************************/
-int open_socket_out(int type, struct in_addr *addr, int port )
+int open_socket_out(int type, struct in_addr *addr, int port ,int timeout)
 {
   struct sockaddr_in sock_out;
-  int res;
+  int res,ret;
+  int connect_loop = 250; /* 250 milliseconds */
+  int loops = (timeout * 1000) / connect_loop;
 
   /* create a socket to write to */
   res = socket(PF_INET, type, 0);
@@ -2831,15 +2896,45 @@ int open_socket_out(int type, struct in_addr *addr, int port )
   sock_out.sin_port = htons( port );
   sock_out.sin_family = PF_INET;
 
+  /* set it non-blocking */
+  set_blocking(res,0);
+
   DEBUG(3,("Connecting to %s at port %d\n",inet_ntoa(*addr),port));
   
   /* and connect it to the destination */
-  if (connect(res,(struct sockaddr *)&sock_out,sizeof(sock_out))<0) {
-    DEBUG(0,("connect error: %s\n",strerror(errno))); 
-    close(res); 
-    return(-1);
+connect_again:
+  ret = connect(res,(struct sockaddr *)&sock_out,sizeof(sock_out));
+
+  /* Some systems return EAGAIN when they mean EINPROGRESS */
+  if (ret < 0 && (errno == EINPROGRESS || errno == EALREADY ||
+        errno == EAGAIN) && loops--) {
+    msleep(connect_loop);
+    goto connect_again;
   }
 
+  if (ret < 0 && (errno == EINPROGRESS || errno == EALREADY ||
+         errno == EAGAIN)) {
+      DEBUG(1,("timeout connecting to %s:%d\n",inet_ntoa(*addr),port));
+      close(res);
+      return -1;
+  }
+
+#ifdef EISCONN
+  if (ret < 0 && errno == EISCONN) {
+    errno = 0;
+    ret = 0;
+  }
+#endif
+
+  if (ret < 0) {
+    DEBUG(1,("error connecting to %s:%d (%s)\n",
+            inet_ntoa(*addr),port,strerror(errno)));
+    return -1;
+  }
+
+  /* set it blocking again */
+  set_blocking(res,1);
+
   return res;
 }
 
@@ -2941,6 +3036,133 @@ BOOL zero_ip(struct in_addr ip)
   return(a == 0);
 }
 
+
+/*******************************************************************
+ matchname - determine if host name matches IP address 
+ ******************************************************************/
+static BOOL matchname(char *remotehost,struct in_addr  addr)
+{
+  struct hostent *hp;
+  int     i;
+  
+  if ((hp = Get_Hostbyname(remotehost)) == 0) {
+    DEBUG(0,("Get_Hostbyname(%s): lookup failure", remotehost));
+    return False;
+  } 
+
+  /*
+   * Make sure that gethostbyname() returns the "correct" host name.
+   * Unfortunately, gethostbyname("localhost") sometimes yields
+   * "localhost.domain". Since the latter host name comes from the
+   * local DNS, we just have to trust it (all bets are off if the local
+   * DNS is perverted). We always check the address list, though.
+   */
+  
+  if (strcasecmp(remotehost, hp->h_name)
+      && strcasecmp(remotehost, "localhost")) {
+    DEBUG(0,("host name/name mismatch: %s != %s",
+            remotehost, hp->h_name));
+    return False;
+  }
+       
+  /* Look up the host address in the address list we just got. */
+  for (i = 0; hp->h_addr_list[i]; i++) {
+    if (memcmp(hp->h_addr_list[i], (caddr_t) & addr, sizeof(addr)) == 0)
+      return True;
+  }
+
+  /*
+   * The host name does not map to the original host address. Perhaps
+   * someone has compromised a name server. More likely someone botched
+   * it, but that could be dangerous, too.
+   */
+  
+  DEBUG(0,("host name/address mismatch: %s != %s",
+          inet_ntoa(addr), hp->h_name));
+  return False;
+}
+
+/*******************************************************************
+ Reset the 'done' variables so after a client process is created
+ from a fork call these calls will be re-done. This should be
+ expanded if more variables need reseting.
+ ******************************************************************/
+
+static BOOL global_client_name_done = False;
+static BOOL global_client_addr_done = False;
+
+void reset_globals_after_fork()
+{
+  global_client_name_done = False;
+  global_client_addr_done = False;
+}
+/*******************************************************************
+ return the DNS name of the client 
+ ******************************************************************/
+char *client_name(void)
+{
+  extern int Client;
+  struct sockaddr sa;
+  struct sockaddr_in *sockin = (struct sockaddr_in *) (&sa);
+  int     length = sizeof(sa);
+  static pstring name_buf;
+  struct hostent *hp;
+
+  if (global_client_name_done) 
+    return name_buf;
+
+  strcpy(name_buf,"UNKNOWN");
+
+  if (getpeername(Client, &sa, &length) < 0) {
+    DEBUG(0,("getpeername failed\n"));
+    return name_buf;
+  }
+
+  /* Look up the remote host name. */
+  if ((hp = gethostbyaddr((char *) &sockin->sin_addr,
+                         sizeof(sockin->sin_addr),
+                         AF_INET)) == 0) {
+    DEBUG(1,("Gethostbyaddr failed for %s\n",client_addr()));
+    StrnCpy(name_buf,client_addr(),sizeof(name_buf) - 1);
+  } else {
+    StrnCpy(name_buf,(char *)hp->h_name,sizeof(name_buf) - 1);
+    if (!matchname(name_buf, sockin->sin_addr)) {
+      DEBUG(0,("Matchname failed on %s %s\n",name_buf,client_addr()));
+      strcpy(name_buf,"UNKNOWN");
+    }
+  }
+  global_client_name_done = True;
+  return name_buf;
+}
+
+/*******************************************************************
+ return the IP addr of the client as a string 
+ ******************************************************************/
+char *client_addr(void)
+{
+  extern int Client;
+  struct sockaddr sa;
+  struct sockaddr_in *sockin = (struct sockaddr_in *) (&sa);
+  int     length = sizeof(sa);
+  static fstring addr_buf;
+
+  if (global_client_addr_done) 
+    return addr_buf;
+
+  strcpy(addr_buf,"0.0.0.0");
+
+  if (getpeername(Client, &sa, &length) < 0) {
+    DEBUG(0,("getpeername failed\n"));
+    return addr_buf;
+  }
+
+  strcpy(addr_buf,(char *)inet_ntoa(sockin->sin_addr));
+
+  global_client_addr_done = True;
+  return addr_buf;
+}
+
 /*******************************************************************
 sub strings with useful parameters
 ********************************************************************/
@@ -2961,8 +3183,9 @@ void standard_sub_basic(char *s)
 
   if (!strchr(s,'%')) return;
 
-  string_sub(s,"%I",Client_info.addr);
-  string_sub(s,"%M",Client_info.name);
+  string_sub(s,"%I",client_addr());
+  if (strstr(s,"%M"))
+    string_sub(s,"%M",client_name());
   string_sub(s,"%T",timestring());
 
   if (!strchr(s,'%')) return;
@@ -3036,7 +3259,7 @@ struct hostent *Get_Hostbyname(char *name)
       return(NULL);
     }
 
-  ret = gethostbyname(name2);
+  ret = sys_gethostbyname(name2);
   if (ret != NULL)
     {
       free(name2);
@@ -3045,7 +3268,7 @@ struct hostent *Get_Hostbyname(char *name)
 
   /* try with all lowercase */
   strlower(name2);
-  ret = gethostbyname(name2);
+  ret = sys_gethostbyname(name2);
   if (ret != NULL)
     {
       free(name2);
@@ -3054,7 +3277,7 @@ struct hostent *Get_Hostbyname(char *name)
 
   /* try with all uppercase */
   strupper(name2);
-  ret = gethostbyname(name2);
+  ret = sys_gethostbyname(name2);
   if (ret != NULL)
     {
       free(name2);
@@ -3083,7 +3306,7 @@ BOOL process_exists(int pid)
     fstring s;
     if (!tested) {
       tested = True;
-      sprintf(s,"/proc/%05d",getpid());
+      sprintf(s,"/proc/%05d",(int)getpid());
       ok = file_exist(s,NULL);
     }
     if (ok) {
@@ -3148,7 +3371,7 @@ my own panic function - not suitable for general use
 ********************************************************************/
 void ajt_panic(void)
 {
-  system("/usr/bin/X11/xedit -display :0 /tmp/ERROR_FAULT");
+  system("/usr/bin/X11/xedit -display ljus:0 /tmp/ERROR_FAULT");
 }
 #endif
 
@@ -3174,14 +3397,12 @@ char *readdirname(void *p)
 
   dname = ptr->d_name;
 
-#ifdef KANJI
   {
     static pstring buf;
     strcpy(buf, dname);
     unix_to_dos(buf, True);
     dname = buf;
   }
-#endif
 
 #ifdef NEXT2
   if (telldir(p) < 0) return(NULL);
@@ -3206,4 +3427,244 @@ char *readdirname(void *p)
 }
 
 
+BOOL is_vetoed_name(char *name)
+{
+  char *namelist = lp_veto_files();
+  char *nameptr = namelist;
+  char *name_end;
+
+  /* if we have no list it's obviously not vetoed */
+  if((nameptr == NULL ) || (*nameptr == '\0')) 
+    return 0;
+
+  /* if the name doesn't exist in the list, it's obviously ok too */
+  if(strstr(namelist,name) == NULL ) 
+    return 0;
+
+  /* now, we need to find the names one by one and check them
+     they can contain spaces and all sorts of stuff so we
+     separate them with of all things '/' which can never be in a filename
+     I could use "" but then I have to break them all out
+     maybe such a routine exists somewhere?
+  */
+  while(*nameptr) 
+    {
+      if ( *nameptr == '/' ) 
+        {
+          nameptr++;
+          continue;
+        }
+      if((name_end = strchr(nameptr,'/'))!=NULL) 
+        {
+          *name_end = 0;
+        }
+      /* a match! it's veto'd */
+      if(strcmp(name,nameptr) == 0) 
+        return 1;
+      if(name_end == NULL) 
+        return 0;
+      /* next segment please */
+      nameptr = name_end + 1;
+    }
+  return 0;
+}
+
+static BOOL is_in_path(char *name, char *namelist)
+{
+  char *nameptr = namelist;
+  char *sub;
+  char *name_end;
+  int len;
+
+  /* if we have no list it's obviously not vetoed */
+  if((nameptr == NULL ) || (*nameptr == '\0')) 
+    return 0;
+
+
+  /* now, we need to find the names one by one and check them
+     they can contain spaces and all sorts of stuff so we
+     separate them with of all things '/' which can never be in a filename
+     I could use "" but then I have to break them all out
+     maybe such a routine exists somewhere?
+  */
+  while(*nameptr) 
+    {
+      if ( *nameptr == '/' ) 
+        {
+          nameptr++;
+          continue;
+        }
+      if((name_end = strchr(nameptr,'/'))!=NULL) 
+        {
+          *name_end = 0;
+        }
+
+      len = strlen(nameptr);
+      sub = name;
+      /* If the name doesn't exist in the path, try the next name.. */
+      while( sub && ((sub = strstr(sub,nameptr)) != NULL)) 
+        {
+           /* Is it a whole component? */
+           if(((sub == name) || (sub[-1] == '/'))
+                && ((sub[len] == '\0') || (sub[len] == '/'))) 
+             {
+               return 1;
+             }
+           /* skip to the next component of the path */
+              sub =strchr(sub,'/');
+         }
+      if(name_end == NULL) 
+        return 0;
+      /* next segment please */
+      nameptr = name_end + 1;
+    }
+  return 0;
+}
+
+/****************************************************************************
+used to make files hidden, but still accessible
+****************************************************************************/
+BOOL is_hidden_path(char *path)
+{
+  return is_in_path(path, lp_hide_files());
+}
+
+/****************************************************************************
+used to make files _completely_ inaccessible
+****************************************************************************/
+BOOL is_vetoed_path(char *path)
+{
+  return is_in_path(path, lp_veto_files());
+}
+
+/****************************************************************************
+routine to do file locking
+****************************************************************************/
+BOOL fcntl_lock(int fd,int op,uint32 offset,uint32 count,int type)
+{
+#if HAVE_FCNTL_LOCK
+  struct flock lock;
+  int ret;
+
+#if 1
+  uint32 mask = 0xC0000000;
+
+  /* make sure the count is reasonable, we might kill the lockd otherwise */
+  count &= ~mask;
+
+  /* the offset is often strange - remove 2 of its bits if either of
+     the top two bits are set. Shift the top ones by two bits. This
+     still allows OLE2 apps to operate, but should stop lockd from
+     dieing */
+  if ((offset & mask) != 0)
+    offset = (offset & ~mask) | ((offset & mask) >> 2);
+#else
+  uint32 mask = ((unsigned)1<<31);
+
+  /* interpret negative counts as large numbers */
+  if (count < 0)
+    count &= ~mask;
+
+  /* no negative offsets */
+  offset &= ~mask;
+
+  /* count + offset must be in range */
+  while ((offset < 0 || (offset + count < 0)) && mask)
+    {
+      offset &= ~mask;
+      mask = mask >> 1;
+    }
+#endif
+
+
+  DEBUG(5,("fcntl_lock %d %d %d %d %d\n",fd,op,(int)offset,(int)count,type));
+
+  lock.l_type = type;
+  lock.l_whence = SEEK_SET;
+  lock.l_start = (int)offset;
+  lock.l_len = (int)count;
+  lock.l_pid = 0;
+
+  errno = 0;
+
+  ret = fcntl(fd,op,&lock);
+
+  if (errno != 0)
+    DEBUG(3,("fcntl lock gave errno %d (%s)\n",errno,strerror(errno)));
+
+  /* a lock query */
+  if (op == F_GETLK)
+    {
+      if ((ret != -1) &&
+         (lock.l_type != F_UNLCK) && 
+         (lock.l_pid != 0) && 
+         (lock.l_pid != getpid()))
+       {
+         DEBUG(3,("fd %d is locked by pid %d\n",fd,lock.l_pid));
+         return(True);
+       }
+
+      /* it must be not locked or locked by me */
+      return(False);
+    }
+
+  /* a lock set or unset */
+  if (ret == -1)
+    {
+      DEBUG(3,("lock failed at offset %d count %d op %d type %d (%s)\n",
+              offset,count,op,type,strerror(errno)));
+
+      /* perhaps it doesn't support this sort of locking?? */
+      if (errno == EINVAL)
+       {
+         DEBUG(3,("locking not supported? returning True\n"));
+         return(True);
+       }
+
+      return(False);
+    }
+
+  /* everything went OK */
+  DEBUG(5,("Lock call successful\n"));
+
+  return(True);
+#else
+  return(False);
+#endif
+}
+
+/*******************************************************************
+lock a file - returning a open file descriptor or -1 on failure
+The timeout is in seconds. 0 means no timeout
+********************************************************************/
+int file_lock(char *name,int timeout)
+{  
+  int fd = open(name,O_RDWR|O_CREAT,0666);
+  time_t t=0;
+  if (fd < 0) return(-1);
+
+#if HAVE_FCNTL_LOCK
+  if (timeout) t = time(NULL);
+  while (!timeout || (time(NULL)-t < timeout)) {
+    if (fcntl_lock(fd,F_SETLK,0,1,F_WRLCK)) return(fd);    
+    msleep(LOCK_RETRY_TIMEOUT);
+  }
+  return(-1);
+#else
+  return(fd);
+#endif
+}
+
+/*******************************************************************
+unlock a file locked by file_lock
+********************************************************************/
+void file_unlock(int fd)
+{
+  if (fd<0) return;
+#if HAVE_FCNTL_LOCK
+  fcntl_lock(fd,F_SETLK,0,1,F_UNLCK);
+#endif
+  close(fd);
+}
+