// ******************************************************************************** //
// **Creator:    Darien                Licence:      Diku Merc Terms             ** //
// **Difficulty:    1 hundred million  Snippet:      file descriptor .. stuff    ** // 
// ******************************************************************************** //
// **                           Contact Information                              ** //
// **Yahoo:      ldevil.geo                 Msn:          ldevil@hotmail.com     ** //
// **Aim:        pocketweasle               Email:        ldevil@hotmail.com     ** //
// **Webpage:    http://sandstorm.arthmoor.com                                   ** //
// ******************************************************************************** //
// **Terms of Usage:                                                             ** //
// **Follow the Diku, Merc Licences, also, if you have a snippet helpfile        ** //
// **Put my name in there, if not, leave my name in the source.                  ** //
// **Also, this code is given AS-IS, straight from my mud, so there will be some ** //
// **Effort required to make this work in your mud.                              ** //
// **Install at your own risk, if you have any comments or questions on this     ** //
// **Visit the website mentioned above, and enter the forum, post and bugs or    ** //
// **suggestions there.                                                          ** //
// ******************************************************************************** //

First off, let me begin by telling you that this was more of a pain in the ass to create than
any other piece of code i have recently done, i know, it may not look all that difficult
to write, but when you get down to it, i had to go searching big-time on how todo this.

So that alone gives me a golden star   *   see.. a star!

Second off, what this code does is not limited to, displaying if a file-descriptor is
open or closed, ney...  You can view what info is infact, within said file-descriptor.

So, all its pertinant data, essentaily, if you are in the shell.. and do a 
cd /proc/<pid>/fd
then do a wonderful
stat <some descriptor number>
the information it would return, will be the same here, just, without the moot-point
of doing that.

This also contains a way to clean out 'dead' descriptors, ie, anything left hanging,
and belongs to the UID of the mud, will be killed off.

Personaly, i find that the info_desc command is doing just fine for me to view vital
information.

Now, a note on the socket's hanging, this system CHECKS the descriptor list.
the IMC (if you have it) the internal webserver (if you have it.. again) and your
connection ports, and leaves those alone.

Ofcourse, if there is a socket hanging, but its not related to any of the above
(and is not an open file ... checks that too) well, essentaily, if it doesn't have
a safeguard to stop it from being 'closed/removed' it will be infact that, closed/removed

So essentaily, this does what any immortal would want, clearing the descriptors that are
fubaared, as we all know, if they add up too much, it can/will cause your mud to crash.

So this is a 'safe-guard' there is no garuntee that it still won't crash, or that closing
a file that isn't safe-guarded won't cause a crash.  But its worth it.

I have been running this on my mud for awhile now, no crash's, descriptors are being actively
cleaned.  I can get information about descriptors as i need it(though i never really do, but its
fun to see the data anyways)

so that being said, you'll need to add func_desc_check, func_info_desc, and func_clean_desc to your interpreter command
call table (or your database for those whom have that)

Aswell as to your interp.h

you will want to throw a void clean_desc(void);  to the top of your update.c

And then put clean_desc(); at the bottom of your update_handler

Simple to install that part.

Now, in my main header, it says that you'll have to port data, and i'm not lying, you really do.
this is straight from my mud, so everything will most likely need a change or two..

You will also need to include sys/stat.h  for this to work.  And if you don't have sys/stat.h
Then you are SOL for using this system.

/* Return the mode name*/
char *st_mode(mode_t mode)
{
		if(S_ISSOCK(mode))
		{
			return "Socket";
		}
		else if(S_ISREG(mode))
		{
			return "Regular File";
		}
		else if(S_ISDIR(mode))
		{
			return "Directory";
		}
		else if(S_ISCHR(mode))
		{
			return "character device";
		}
		else if(S_ISBLK(mode))
		{
			return "block device";
		}
		else if(S_ISFIFO(mode))
		{
			return "fifo(?)";
		}
		else 
		{
			return "Unknown/Error";
		}

		// hit the end!
	      return "(Unknown)/Error";
}

// Command for checking descriptor information!
MudCmd( func_desc_check )
{
   int desc;
   int counter = 0;
   int found = 0;
   struct stat bstat;
   BUFFER *output = NewBuf();
   
   BufferF(output, "\n\r`*+`6-------------------------`^Displaying File Descriptors`6-------------------------`*+\n\r" );
   for( desc = 0; desc < 300; desc++ )
   {
      if( fstat( desc, &bstat ) < 0 )
      {
	continue;
      }
      else
      {
              BufferF(output, "`#%3d`7: `!yes``   %30s ", desc, st_mode(bstat.st_mode) );

	      found++;
	      counter++;
	      if(counter == 2)
	      {
		BufferF(output, "\n\r");
		counter = 0;
	      }
      }
   }

   BufferF(output, "\n\r" );
   BufferF(output, "Found %d descriptors open\n\r", found);
   page_send(buf_string(output), ch);
   close_buffer(output);
   return;
}

// Command for infoing a 'said' desscriptor
MudCmd( func_info_desc)
{
   int desc;
   struct stat info;
   BUFFER *output = NewBuf();

   if(!IsNumber(argument))
   {
	ch->Send("Descriptor must be a number!\n\r");
	close_buffer(output);
	return;
   }
   
   BufferF(output, "\n\r`*+`6-------------------------`^Displaying File Descriptor Information`6-------------------------`*+\n\r" );
   for( desc = 0; desc < 300; desc++ )
   {
	if((atoi(argument) == desc))
	{
	    if (fstat(desc, &info) != 0)
		BufferF(output, "Unable to gather information on file descriptor: %d\n\r", desc);
	    else 
	    {
	      BufferF(output, "`*  inode          `$:   `!%d\n\r",   (int) info.st_ino);
	      BufferF(output, "`* dev id          `$:   `!%d\n\r",   (int) info.st_dev);
	      BufferF(output, "`*   mode          `$:   `!%08x`*/`#%s\n\r",       info.st_mode,st_mode(info.st_mode));
	      BufferF(output, "`*  links          `$:   `!%d\n\r",         info.st_nlink);
	      BufferF(output, "`*    uid          `$:   `!%d\n\r",   (int) info.st_uid);
	      BufferF(output, "`*    gid          `$:   `!%d\n\r",   (int) info.st_gid);
	      BufferF(output, "`*   size          `$:   `!%d\n\r",   (int) info.st_size);
	      BufferF(output, "`*block size       `$:   `!%d\n\r",   (int) info.st_blksize);
	      BufferF(output, "`*block count      `$:   `!%d\n\r",   (int) info.st_blocks);
	      BufferF(output, "`*Last Access      `$:   `!%s/%s\n\r",   grab_time_log(info.st_atime), grab_date_log(info.st_atime));
	      BufferF(output, "`*Last Modification`$:   `!%s/%s\n\r",   grab_time_log(info.st_mtime), grab_date_log(info.st_mtime));
	      BufferF(output, "`*Last Change      `$:   `!%s/%s\n\r",   grab_time_log(info.st_ctime), grab_date_log(info.st_ctime));
	      BufferF(output, "``\n\r");
	   }
       }
   }

   page_send(buf_string(output), ch);
   close_buffer(output);
   return;
}

// clean a selected descriptor
MudCmd( func_clean_desc)
{
   int desc = 0, cleaned = 0;
   struct stat info;
   BUFFER *output = NewBuf();
   extern int sockfd;
   
   BufferF(output, "\n\r`*+`6-------------------------`^Cleaning File Descriptors `6-------------------------`*+\n\r" );
   for( desc = 0; desc < 300; desc++ )
   {
	    list<descriptor_data *>::iterator dsc, dsc_next;
	    bool no_remove = false;
	    if (fstat(desc, &info) != 0)
	    {
		continue;	// Okay, nothing on this descriptor
	    }
	    else 
	    {
		if(info.st_uid != getuid ())
		{
			continue;
		}

		if(S_ISSOCK(info.st_mode))
		{
			// Are we the mud-control, web-control, and imc
			if(desc == mud_data.control || desc == mud_data.control_2 || desc == sockfd || desc == this_imcmud->desc)
			{
				no_remove = true;
				break;
			}

			// This evil method *SHOULD* kill the bad descriptors :)
			for(dsc = descriptor_list.begin(); dsc != descriptor_list.end(); dsc = dsc_next)
			{
				descriptor_data *d = (*dsc);
				dsc_next = ++dsc;

				// Found a potential! No need to clean!
				if(desc == d->descriptor)
				{
					no_remove = true;
					break;
				}

				if(no_remove =compare_web(desc))
				{
					break;
				}
			}

		}
		else if(S_ISREG(info.st_mode))
		{
			no_remove = true;
		}
		else if(S_ISDIR(info.st_mode))
		{
			no_remove = true;
		}
		else if(S_ISCHR(info.st_mode))
		{
			no_remove = true;
		}
		else if(S_ISBLK(info.st_mode))
		{
			no_remove = true;
		}
		else if(S_ISFIFO(info.st_mode))
		{
			no_remove = true;
		}
		else 
		{
			no_remove = false;	// Yuppers, kill this file!
		}

	      if(!no_remove)
	      {
			close(desc);	// Close the descriptor
			desc = -1;	// Ensure the descriptor is now -1
			cleaned++;
	      }
       }
   }

   BufferF(output, "Cleaned %d descriptors", cleaned);
   page_send(buf_string(output), ch);
   close_buffer(output);
   return;
}

// Update cleaner!
void clean_desc(void)
{
   int desc = 0, cleaned = 0;
   struct stat info;
   extern int sockfd;
   
   for( desc = 0; desc < 300; desc++ )
   {
	    list<descriptor_data *>::iterator dsc, dsc_next;
	    bool no_remove = false;

	    if (desc == 0 || desc == 1 || desc == 2 || desc == 3 || desc == 4 || desc == 5 || desc == 6)
		continue;
	    if (fstat(desc, &info) != 0)
	    {
		continue;	// Okay, nothing on this descriptor
	    }
	    else 
	    {
		if(info.st_uid != getuid ())
		{
			continue;
		}

		if(S_ISSOCK(info.st_mode))
		{
			// Are we the mud-control, web-control, and imc
			if(desc == mud_data.control || desc == mud_data.control_2 || desc == sockfd || desc == this_imcmud->desc)
			{
				no_remove = true;
				break;
			}

			// This evil method *SHOULD* kill the bad descriptors :)
			for(dsc = descriptor_list.begin(); dsc != descriptor_list.end(); dsc = dsc_next)
			{
				descriptor_data *d = (*dsc);
				dsc_next = ++dsc;

				// Found a potential! No need to clean!
				if(desc == d->descriptor)
				{
					no_remove = true;
					break;
				}

				if(no_remove =compare_web(desc))
				{
					break;
				}
			}

		}
		else if(S_ISREG(info.st_mode))
		{
			no_remove = true;
		}
		else if(S_ISDIR(info.st_mode))
		{
			no_remove = true;
		}
		else if(S_ISCHR(info.st_mode))
		{
			no_remove = true;
		}
		else if(S_ISBLK(info.st_mode))
		{
			no_remove = true;
		}
		else if(S_ISFIFO(info.st_mode))
		{
			no_remove = true;
		}
		else 
		{
			no_remove = false;	// Yuppers, kill this file!
		}

	      if(!no_remove)
	      {
			sstm_log(LOG_BUG, "Cleaning Hanging Descriptor: %d st_mode: %s: %08x", desc, st_mode((mode_t)info.st_mode), info.st_mode);
			close(desc);	// Close the descriptor
			desc = -1;	// Ensure the descriptor is now -1
			cleaned++;
	      }
       }
   }

   return;
}