Mastodon Politics, Power, and Science

Friday, November 2, 2012

Looking at firmware update files and compiling files.

Update:  I was finally successful in replacing my old disabled by the corporation media box... Raspberry Pi using the Raspbmc distribution:  http://mystry-geek.blogspot.com/2013/05/finally-got-raspberry-pi-to-try-out.html


It is obvious that nobody ever managed to compile the little bit of code that netgear released for the eva2000.   And nobody ever got a firmware file to directly update the little box.  I am not even positive it can update other than over the network.  

Trying to find and extract out enough binaries from various similar boxes to my vendor deactivated netgear eva2000 in order to install a new firmware to the system.

I used the following command to see if the system could ID the file:

file binaryfilename.bin

And it told me:


So I took a look at the contents of the file directly with a hex viewer:

od -ah binaryfilename.bin | 

the output from that is:

0000000   e   a   f   b   2   6   b   c   0   b   1   9   d   3   f   1
           6165    6266    3632    6362    6230    3931    3364    3166
0000020   0   4   6   3   4   3   2   5   4   a   5   e   f   e   4   1
           3430    3336    3334    3532    6134    6535    6566    3134
0000040   E   =   M   ( nul   @ dc1 etx etx nul nul nul nul nul nul nul
           3d45    28cd    4000    0311    0003    0000    0000    0000
0000060   C   o   m   p   r   e   s   s   e   d  sp   R   O   M   F   S
           6f43    706d    6572    7373    6465    5220    4d4f    5346
0000100  fs   [  so   T nul nul nul nul   k   & nul nul   K  so nul nul
           db9c    d40e    0000    0000    266b    0000    0ecb    0000
0000120   C   o   m   p   r   e   s   s   e   d nul nul nul nul nul nul
           6f43    706d    6572    7373    6465    0000    0000    0000
0000140   m   A   l etx   D soh nul   d   @ eot nul nul   m   A   l etx
           41ed    03ec    0144    6400    04c0    0000    41ed    03ec

I could see the start of the compressed file system started 32 bytes in with the byte pattern "3d45    28cd", the header must have some sort of checksum or other meta data about the install.  So I extracted the file following the leading 32 bytes with:

dd if=binaryfilename.bin bs=1 skip=32 of=test.fs

And I was finally able to mount the compressed filesystem:


mkdir m #gives a place to mount the device
sudo mount -t cramfs test.fs m  # mounts the file test.fs onto m.

The you can browse into m and see everything installed on that drive.



I did a full scan of all open ports on the eva2000 with nmap

sudo nmap  -p 1-65535 192.168.1.70


 Starting Nmap 5.21 ( http://nmap.org ) at 2012-11-02 22:44 EDT
Nmap scan report for unknown0026f23a9297.att.net (192.168.1.70)
Host is up (0.0033s latency).
Not shown: 65533 closed ports
PORT      STATE SERVICE
51887/tcp open  unknown
63681/tcp open  unknown
MAC Address: 00:26:F2:3A:92:97 (Netgear)

Nmap done: 1 IP address (1 host up) scanned in 12.92 seconds


and saw the following two TCP ports open:  51887 and 63681

Telneting into both ports and hitting return a few times gave me this error:

>telnet 192.168.1.70 51887

Trying 192.168.1.70...
Connected to 192.168.1.70.
Escape character is '^]'.


HTTP/1.1 412 Failed
Server: Verismo, POSIX, DLNADOC/1.00 INTEL_NMPR/2.1 UPnP/1.0 Intel MicroStack/1.0.1677
Content-Length: 0


> telnet 192.168.1.70 63681

Trying 192.168.1.70...
Connected to 192.168.1.70.
Escape character is '^]'.



Connection closed by foreign host.


Connecting a web browser to both of them just gives an xml output on 63681:

<root><specVersion><major>1</major><minor>0</minor></specVersion><device><deviceType>urn:schemas-upnp-org:device:MediaRenderer:1</deviceType><X_DLNADOC>urn:schemas-dlna-org:device-1-0</X_DLNADOC><friendlyName>Netgear EVA2000</friendlyName><manufacturer>NETGEAR</manufacturer><manufacturerURL>http://www.netgear.com</manufacturerURL><modelDescription>Digital Entertainer Live</modelDescription><modelName>EVA2000</modelName><modelNumber>EVA2000</modelNumber><serialNumber> </serialNumber><UDN>uuid:[redacted]</UDN><serviceList><service><serviceType>urn:schemas-upnp-org:service:AVTransport:1</serviceType><serviceId>urn:upnp-org:serviceId:AVT_1-0</serviceId><SCPDURL>AVTransport/scpd.xml</SCPDURL><controlURL>AVTransport/control</controlURL><eventSubURL>AVTransport/event</eventSubURL></service></serviceList></device></root>

Which appears to be a DLNA server.

--

I will try to wireshark after a soft reboot to see what server it looks for an update on.  If I can go to that server myself it might tell me something.  And I might be able to do a man in the middle attack on my own box to intercept any encrypted communcation if they do it over https.

Wednesday, October 31, 2012

My Netgear EVA2000 went into strange mode 1 day after being hooked to Internet

Update:  I was finally successful in replacing my old disabled by the corporation media box... Raspberry Pi using the Raspbmc distribution:  http://mystry-geek.blogspot.com/2013/05/finally-got-raspberry-pi-to-try-out.html


Let me know if you owned a netgear product that might have also been deactivated in this way.

So, I finally hooked my Netgear EVA2000 multimedia internet device up to the internet after having owned it for a couple of years and the day after I hooked it up, it crapped itself.  Evidently they phone home when they are connected to the Internet, and it got some sort of message from home to crap itself.

 I searched on the Internet and a lot of people have had this same problem with no  resolution from Netgear.  So I decided to give Netgear support a shot.  Wish me luck.


Online technical submission: view case
Case #:      [redacted]
Problem:      Functionality
Cause:      ReadyNAS - Streaming Media
Status:      Open
Notes:     
10/31/2012 3:08:00 PM
I have had [ my netgear eva2000 ] hooked to my tv for about 2 years now and it has worked flawlessly to play movies off a hard drive and to play movies from a media server through the internet without any issues.

I finally got an internet connection and hooked the device to the internet so I could get access to the other functions. The day after it had access to the internet it updated the firmware without asking me and all the functions on the device no longer works. The main menu comes up as: My Collection Movies on Demand Internet video Settings And only settings works, clicking on the others just moves the cursor down to settings. There used to be a youtube option and it is now gone. Settings works fine, but having a little box hooked to my TV that _only_ allows me to change its settings is sort of useless.

A soft reset and a hard reset does nothing to resolve the issue.  [evidently they call it a soft restore and a full restore]

The only way I can get to my local media server now is to goto Settings->Eject USB disk, wait a few seconds, plug the drive back in, wait a minute or two, hit OK to browse the disk when that finally comes up. This window goes away quickly, so I am forced to stand there like an idiot for a couple of minutes waiting for this screen, or I have to start the process all over again. I am finally sitting at the screen where I can select my hard drive, if I press back at this point I can get access to the menu that my collections should take me to.

To sum it all up, it appears that connecting to the internet allowed my box to phone home looking for an update and it was ordered into this mode by your company. Needless to say, I am upset that you deliberately broke a perfectly functional unit, with what I can only assume is a desire to sell me a newer product. If you cannot fix my eva2000 back to a working state, or provide me with a working replacement unit, I will never purchase any product from your company again. I will instruct all my friends and family to not buy your products. On every internet forum that discusses your products I will tell my story about how it appears that you deliberately crippled my device at end of life in order to force me to purchase a newer product. Good day.

10/31/2012 8:23:00 PM
From Expert ID: [redacted]

Case ID: [redacted]

Dear Mr.Rogers,

Thank you for choosing NETGEAR. My name is Rajesh, and I will be your support expert today.

I understand that you are experiencing a problem with EVA2000. We apologize for this inconvenience. Because we are doing this online, it might require a few email exchanges to resolve the issue. Rest assured that we will do our best to resolve your case quickly.

Use this option to restore factory defaults.

1. Select Restore Factory Defaults on the Settings screen.
2. You can select one of the following:

a. Soft Restore – Resets the factory defaults, removes your user account, but preserves all
downloaded movies (both purchased and rented).
b. Full Restore – Resets the factory defaults, removes your user account, and all
downloaded movies (both purchased and rented).

Warning: Because the Full Restore removes your user account and BOTH rented and purchased movies, NETGEAR recommends that you use the Soft Restore option for most purposes.

3. Click the Select ok button to confirm your selection.
DE Live returns you to the Settings screen.

Followed by the following procedure please hard reset by holding the power button for 8 seconds

Please contact us again if you require further assistance.

Please do visit http://support.netgear.com for any technical queries regarding NETGEAR products.

A notice will automatically be sent to your email address when we have responded to your inquiry. Please DO NOT REPLY to that email. Instead, to add additional information to your case, click No to the question "Was your problem resolved with the information provided by the NETGEAR representative above?"

If you click YES, your case will be closed and a separate email containing a survey link will be sent so you can share with us your customer support experience.

Thanks again for choosing NETGEAR. Have a great day!

Sincerely,
Rajesh.

Technical Support
NETGEAR, Inc.
http://my.netgear.com

***NOTE: Your case will autoclose after 7 days of inactivity.***

Did you know that NETGEAR provides support for all your home networking devices and PCs? We can provide a one-stop solution - no need to call multiple vendors* for support. If you would like to learn more about the NETGEAR GearHead services, go to http://kbserver.netgear.com/kb_web_files/gearhead/home.html.

31 Oct 2012 06:51pm

I replied that I tried the above and it did not fix my problem, asked about a firmware update, and was given this response:

Your request has now been sent to a support representative


So there is still hope.   Just in case I am looking at making a little xbmc box to hook to my TV, With just a handful of  plugins it would be far more capable than the little netgear device.  I really liked how convenient and low powered that little device was though, so it it still disappointing that

--

Got a reply back after a few hours, they admit it is a defective product.

My name is Rajesh, and I am following up on your Support case.

After reviewing the information you provided, I have a better understanding of your issue and believe I can resolve this for you. Please follow the steps below:

Regarding your concern since this product is the end life product we will not be getting updates anymore and we are sorry to inform you that the product is defective so We apologize for this inconvenience caused.

Sorry to inform you that the product is out of hardware warranty, hence it is not possible for us to replace the product. Please find the warranty information below.

Hardware: Dec 22, 2011 (-314)
Software: Mar 22, 2011 (-589)
Power Supply: Dec 22, 2011 (-314)
Accessories: Dec 22, 2011 (-314)

We apologize for the inconvenience caused.

 --

I am accusing them of placing a backdoor into a computer system that allowed them to remotely deactivate the machine once it hit end of life.

Let me know if you owned a netgear product that might have also been deactivated in this way.

--

Day 3, still just being politely told to piss up a rope.  They are not letting me talk to any manager or tier II support, despite numerous requests from me.

--

Friday, October 26, 2012

I ordered a 3D printer

A Replicator 2 from Makerbot. It is sturdily built and is ready to print in less than an hour after being taken out of the box. The only downside is that it is more expensive and more of a "prosumer" level printer than the others. It also uses a renewable plastic called PLA which has characteristics different than that of the ABS plastics.

Looking at the less expensive printers, I was not impressed by their stability and repeatable accuracy. And the time it took to begin getting reliable prints was too high for my needs. 

My plan is to learn how to make computer models, learn how to do 3d scanning of real items, and then print those models precisely on the printer.  And to have that mastered in about 6 months.  I will also be printing parts out from thingiverse.com.

 I won't get the printer for a month or two.  Waiting is tough.

Using Orange to generate association rules for a dataset.


by James M. Rogers on Friday, October 26, 2012 at 3:59am ·
MIS 420 Data Warehousing
26 Oct 2012

Everything I know about association knowledge mining in Orange comes from this web page: http://orange.biolab.si/doc/ofb/assoc.htm. Let's discuss the incoming data first, then talk about how the data is processed into rules, and finally discuss what we can do with the rules once we have them. 


In the examples we used the datafile imports-85.tab. If you click on that link you will see the data file is set up in a tabular format, with tabs between each attribute or field and a newline between each data tuple or record. Once the data is set up in the tabular format you can bring the data into the program to be processed:

import Orange  
data = Orange.data.Table("imports-85.tab")  
The data must not be continuous in a column. If it is, then you have to categorize the column. You can use the following command to convert continuous fields into discrete sets of three equally populated intervals:
data = orange.Preprocessor_discretize(data, method=orange.EquiNDiscretization(numberOfIntervals=3))
To select a sub set of the data, such as the first ten columns you can specify a range like so:
data = data.select(range(10))
At this point you can convert the data into a set of rules and calculate the confidence, support and lift of every rule using orange.AssociationRulesInducer like so:

rules = orange.AssociationRulesInducer(data, support=0.78)
The data is just the set of data we have imported, maybe converted to discrete values, and maybe sub-selected a range of values. The support is the minimum support we are going to accept as a rule. If the support is equal to or greater than this support value, the rule is dropped. This conversion is based on the APRIORI algorithm from Agrawal et al.'s Fast discovery of association rules, a chapter in Advances in knowledge discovery and data mining published in 1996. This algorithm is optimized to work on the tabular format we imported. Class variables are treated like attributes by this function.

Now that we have the rules in the list named rule we can now work with the list just like any other python list. The orngAssoc module is already written to work with the rules and includes two functions to conveniently work with the rule set directly, from http://orange.biolab.si/doc/modules/orngAssoc.htm:
printRules(rules, ms = []) Prints out the rules. If ms is left empty, only the rules are printed. If ms contains rules' attributes, e.g. ["support", "confidence"], these are printed out as well.
sort(rules, ms = ["support"]) Sorts the rules according to the given criteria. The default key is "support"; you can list multiple keys.
You can also select() parts of the rule list, del() rules, append() rules.
Once you have the rules you can then filter on confidence and lift using the python lambda function, which is an way to do an unnamed function:
conf = 0.8; lift = 1.1
print "\nRules with support>%5.3f and lift>%5.3f" % (conf, lift)
rulesC=rules.filter(lambda x: x.confidence>conf and x.lift>lift)
rulesC now contains only the rules with confidence above 0.8 and lift above 1.1. Then you can sort by confidence and print out the confidence, support, lift and the rule:
orngAssoc.sort(rulesC, ['confidence'])
orngAssoc.printRules(rulesC, ['confidence','support','lift'])
These orange functions make generating association rules from tabular data almost trivially easy. You can import the data, convert continuous columns to discrete columns, and finally generate rules with confidence, support and lift. Once the rules are generated it is easy to sort, filter, and select subsets of the data and then print out the results you require.

Tuesday, August 7, 2012

Memory Management

by James M. Rogers
Summer of 2012

Any application will have data that it must store in memory in order to fulfil its purposes.  The application will acquire this data from the hard drive, from another system, from a database, from a sensor, or from procedural generation. 

To process this data rapidly you must load the data into memory.  Even if you just load a small window into the data into memory you still have to allocate and release the memory as needed as you effectively marshal the data from storage.

When the results are calculated, the resultant data set must also be saved to a storage area so that it does not have to be recalculated.  Or it needs to be deleted in order to make room for the next data set that is processed. 

This process is called memory management.

I began writing down my experiences with memory management in order to document my research and findings into memory management techniques for computer program.

Often the way in which a program accesses memory will determine it's maximum performance.  The way that memory is allocated and used is so integral to a programs design that choosing an improper algorythm initially will lead to serious performance during operation.  In order to fix the issue the program will have to be redesigned and rewritten which takes significant resources and time.

Matching the data set size to the proposed algorthym in a test harness is one tool that can be used to allow a program to properly evaluate models before implementing full programs.  The 80/20 rule here can be used to great effect, allowing many possible senarios to be proposed and tried before deciding on one final solution.  This is not wasted time.  The test harnesses and prewritten algorthms should be thoroughly documented and be kept inside a source code control repository to make later tests for other projects much more efficient.  Many bits will end up in the later programs.  The data generator that creates the databases for use can be made into generic utilities customized for your systems.

Another area of testing that is often overlooked is to start with a smaller data set, begin running the program, and test for bottlenecks as the dataset is grown up to full production values.  Then have the system remove the data and grow it back up again.  Do this several dozen times and see if anything bad happens as disk files and databases expand and contract back down again.


--


Common types of failures.


Memory Leak.

A failure to properly allocate and release memory is called a memory leak.  Eventually your process must be stopped and restarted because it has grown to a size that exceeds both real and virtual memory sizes.  Growing beyond real memory will impact system performance.


Data Corruption, or Program Crash

A second failure mode with improperly allocation and releasing of memory is trying to use memory that has already been freed.  Worst case is that your program continues without crashing, best case is that you get a warning about the problem because your program has crashed.


Fragmentation.

Many programs will alloc a block of memory when required, and free it when that block is no longer used.  They will do this with various sized blocks of memory.  This will cause gaps to grow between allocated blocks.  Eventually you will request a block of data and the system call will fail, despite there appearing to be plenty of room for your files.  This failure mode can impact performance for the entire system and even cause memory allocation failures in other programs.


Failure to handle an error return.

You must not assume that your data request for more memory was successful.  It is possible that your request was denied and you must properly handle the failure, passing the error return back up the stack so that the program can manage and recover from the errors.

Realloc failures.

One of the issues I recently ran into was in reallocating a large block of memory in a program. As memory usage grew the program kept reallocating larger and larger blocks of memory.

The method I was using worked well for a while, but as the program exceeded real memory and had to turn to virtual memory the system suddenly ground to a halt and began paging memory out to disk.  What had taken just a few milliseconds suddenly began taking seconds to complete.  This resulted in the program suddenly going from being able to complete processing from seconds to minutes, 60 times slower.

A second issue is that in order to realloc a block the system has to allocate a larger block, copy the old block to the new block and then release the old block.  What this means is that when you have a 400MB block of memory allocated and you reallocate it to 600MB, then 1GB of memory will be in use at the same time until the smaller block can be freed.  The same effect can happen when shrinking blocks of memory as happens when growing the blocks.

These issues make realloc less than well behaved when dealing with large blocks of memory.

--


Techniques to properly manage memory.


Keep a list of freed nodes around.

One technique that I have used successfully in the past to to write my program just like normal, allocing and freeing nodes inside a couple of functions.  I noticed that I was rapidly creating and freeing nodes all the time.  Then later I would change the delete_node() function to add the nodes to a free list, and change the new_node() function to first try to grab a node from the free list, only allocating a new node when the free list was empty.  Often a program will reach a steady state and overall will just be placing and removing nodes from the free list.

In that particular case I was able to easily double performance.    Of course this was a little naive and did not incorporate any garbage collection to release free nodes once the free list grows to a certain size, but that could also be easily handled.  I'd recommend having a high and low water count.  When the size grew up to the high water mark, then delete down to the low water mark.  Not all at once, just a few at a time so that your program doesn't seem to randomly freeze up as it free's a large number of blocks at a time.


Pool.

It is just as fast to allocate a large block of memory as it is to allocate a single small block.  Allocating large blocks of memory also helps fight memory fragmentation issues at the system level.

Typically each kind of object will get its own pool of memory and a bit map is used to see which block is free. Because every object is the same size in the pool, then you cannot fragment your memory.

It is common to chain blocks of the same sized pool together when you need to allocate more room.

Another interesting technique is to use a single pool for the entire program, but do a powers of 2 size aggregation so that one byte allocations are in one group, two byte are in another, 4 byte are in a third, 8 byte sized elements all are allocation from a fourth, 16 byte sized allocations occur from a fifth, 32, 64, 128, 256, 512, 1024, 2048... and so on, up to a set size.

If you ask for a block from pool of size 400, then you will get a block of size 512, and the excess will be wasted, but the trade off is in speed and performance and the lack of fragmentation.

Managing these pools internally is interesting.  You have to look at the location of the memory in order to match it will the proper pool so that you can free the memory in the proper pool.

The proper function of the Pool you go with has to be assured with comprehensive tests at the function and unit testing.


Management of stream data.

Data coming from a file, network port, or sensor will often need to be processed in the same order that it is received and the way you read a network port tends to cause the data to be "chunky." 

I have had great luck by using a block of memory much larger than the data it is holding at any one time as an I/O buffer.  I write the data to the end of the buffer, and read from the front of the buffer.  When the data reaches the end of allocated memory, then memmove the entire block back to the front of the buffer, and write to the end.  When the buffer is full, double the size of the block of memory with realloc.  When the size of the data in the buffer is 1/4 the size of the buffer, half the buffer size.

You can extend the functionality of the stream object to get a line at a time, a record at a time, a word at a time, or even a field at a time if you give it the delimiter to look for.  This means that this one object is pretty good at tokenizing a stream of data into discrete tokens with meaning.  This more than doubles performance, because just getting data out of the stream has already processed it for you.

Queue's

By using one stream data object to hold the size of the data you just stuck into a second second stream data object you can effectively simulate a queue object with FIFO and LIFO and mixed processing modes. 

One system I worked on had a queue object, and that queue was at the top of the list when we profiled the program.  I replaced the queue object with a queue allocated using my technique and saw the queue functions drop to the bottom of the profile list.  In fact, my friend wanted to stress test my changes and had the queue insert 50 times and release 50 times each call and it still didn't budge from the bottom of the profile list.

Wednesday, July 18, 2012

Fibonacci Numbers

Wrote a couple of versions of Fibonacci number generators. 

Interestingly, the numbers become inaccurate at just the 93rd fib number in the sequence: the real numbers begin losing precision on the low end.   I am going to have to figure out how to use one of the bignum libraries in order to work exactly with such large values.  My fear is that the bignum libraries have a substantial performance hit in return for the accurate values.

The recursive version becomes too slow to use at just the 40th number in the sequence, while the non-recursive version fires through thousands of values.


Fib-recursive.c
 
#include <stdio.h>

/* This program calculates fibonacci sequence using recursion

 1 1 2 3 5 8 13 21 34
 
 Starting with 1
 Each number in the sequence is created by adding the previous two numbers in sequence.
 
*/


long double fib(long double value){
  if (value <= 2)
    return 1;
    
  return (fib(value-1) + fib (value-2));
}

int
main(){

long double i;

  for (i = 1; i<1000; i++)
     printf ("value is %.0Lf and return value is %.0Lf\n", i, fib(i)); 

}
 
 
 
Fib-nonrecursive.c
 
#include <stdio.h>

/* This program calculates fibonacci sequence using recursion

 1 1 2 3 5 8 13 21 34
 
 Starting with 1
 Each number in the sequence is created by adding the previous two numbers in sequence.
 
 If you notice, something bad happens when we reach number 93 and we lose precision off the end.
 The number is close to the right number but is subtly wrong.
 
 We need to switch to a bignum library to add numbers that large precisely.
 
 Interestingly we can calculate the first thousand numbers in the sequence this way before we can calculate the first 30 numbers recursively.
 
*/


int
main(){

long double i, j, p1, p2;

  p1 = 0;
  p2 = 1;
  i  = 0;
  j = 1;
  
  while (j<1000){
    i = p1 + p2;
    printf ("\t%.0Lf\t%.0Lf\n", j, i);
    p2 = p1;
    p1 = i;
    j++;
  }

} 
 
 

Monday, July 9, 2012

Simple web server using epoll and sendfile.

I have been looking at how to use sendfile() and epoll() for a while now, so I finally sat down and wrote a very simple web server that uses these two sets of functions.  The resulting code follows this text.  This web server just performs a get request, and returns the file at the requested path.  If the file cannot be found, it returns a 401 error, file not found.

sendfile() is useful because it dramatically speeds up and simplifies reading a file from disk and sending it out the network file descriptor.   Typically a web server has to have the file and the network socket open, and then read a block from the file, then write the block to the disk, this results in the data being copies between several buffers and the buffer management involved in that.   However the sendfile() man page recommends to fall back to the normal read and write in cases where


epoll() is great because it doesn't have the overhead of select, or most of the limitations.  With select you have to loop through every element in a large, fixed size array, testing each one to see if you got a connection on that element.   With epoll you get a list of elements to handle that you can quickly process, limiting unproductive looping on the possibly thousands of elements you  did not have to process.  Epoll can also easily handle thousands of simultaneous connections without slowing down like select would.


This program does not perform cgi, send mime types, or perform any http request other than get.    It assumes that the first data block of the request contains the entire request.  The program assumes one request per connection.  It ignores any trailing text after the first line.  The beauty is in it's compiled size, it is just 9.5KB in size and is very easy to understand.  


This program only handles a single request at a time because the sendfile request blocks.

Next steps:
  1. Break code into generic epoll server file and server specific files, with call backs in the generic epoll side to the server side to handle new connect, data request, and closed connections.  This will allow the generic epoll server stuff to be completely generic and usable for various other projects.  If it is a library then this could be shared among multiple executing programs.
  2. Handle the request correctly to entirely process the request header and attach all the data to the connection, then perform the request by the request type.
  3. Handle mime types.  Each file should have a mimetype header sent before the data based on the extension of the file.
  4. Handle things other than just "GET".
  5. Handle multiple requests on a single connection.
  6. Add becoming a deamon and signal handling to code to properly handle various tasks.
  7. Use a worker thread pool to get requests and process them while the main process just handles new connections, reading client requests, and closing old connections.   The worker threads can each handle a single completed request, sending the data to the client through the port.
  8. Handle the reading of the input and processing of requests in the worker thread as well, this would leave the main thread just handling new connections passing triggered events to threads.
  9. Handle cgi programs.  I already have code to do this, just needs converted to also use epoll() to monitor the open handles and process events.
  10. Add configuration file reading.
  11. Add database reading/writing for persistent data across connections using sqlite.  Allow get and put requests using the database.



/*

This is a very simple web server that listens for connections on epoll.

It only processes the first line and only understands GET and the path.

Web server will flag the OS to send file to port.

Root is the Root directory just above the location of the executabe.

*/

#include <sys/sendfile.h>
#include <pthread.h>
#include <stdio.h>
#include <sys/timeb.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#include <stdlib.h>

#define MAX_CLIENT 5000
#define PORT 8080

void nonblock(int sockfd)
{
  int opts;
  opts = fcntl(sockfd, F_GETFL);
  if(opts < 0)
  {
    perror("fcntl(F_GETFL)\n");
    exit(1);
  }
  opts = (opts | O_NONBLOCK);
  if(fcntl(sockfd, F_SETFL, opts) < 0) 
  {
    perror("fcntl(F_SETFL)\n");
    exit(1);
  }
}


void
Initialize(){

}


/* Parse and return file handle for http get request */
int
OpenFile(char * buf, int n){

  int i, j;

  char * index = {"index.html"};

  char path[1024];

  if (buf==NULL || n<10)
    return 0;

  // Ensure that the string starts with "GET "
  if (!buf[0]=='G' || !buf[1]=='E' || !buf[2]=='T' || !buf[3]==' ')
    return 0;

  // copy the string
  for (i=0; i < n && buf[i+4]!=' '; i++)
    path[i]=buf[i+4];  

  // put null on end of path
  path[i]='\x00';

  // if the syntax doesn't have a space following a path then return
  if (buf[i+4]!=' ' || i == 0)
    return 0;

  // if the last char is a slash, append index.html 
  if (path[i-1] == '/')
    for (j=0; j<12; j++)
        path[i+j]=index[j];

  printf("Found path: \"%s\"\n", path);

  // the +1 removes the leading slash
  return (open(path+1, O_RDONLY));


}

void
Respond(int fd, char * buf, int n){

  char badresponse[1024] = {"HTTP/1.1 404 Not Found\r\n\r\n<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\r\n<TITLE>Not Found</TITLE></HEAD><BODY>\r\n<H1>Not Found</H1>\r\n</BODY></HTML>\r\n\r\n"};
  char goodresponse[1024] = {"HTTP/1.1 200 OK\r\n\r\n"};

  int fh; 

//  printf("%d data received: \n%s\n", fd, buf);
  
  fh = OpenFile(buf, n);

  if (fh > 0){

    struct stat stat_buf;  /* hold information about input file */

    send(fd, goodresponse, strlen(goodresponse), 0);

    /* size and permissions of fh */
    fstat(fh, &stat_buf);
    sendfile(fd, fh, NULL, stat_buf.st_size);
    close(fh);

  } else {

    send(fd, badresponse, strlen(badresponse), 0);
  }
  close(fd);
}


void
Respond_simple( int fd, char * buf, int n){

  char response[1024] = {"HTTP/1.1 200 OK\r\n\r\n<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\r\n<TITLE>301 Moved</TITLE></HEAD><BODY>\r\n<H1>Hello World!</H1>\r\n</BODY></HTML>\r\n\r\n"};
  int y = strlen(response);

  printf("%d data received: \n%s\n", fd, buf);
  send(fd, response, y, 0);
  close(fd);

}

void
Loop(){

  struct epoll_event *events;
  struct sockaddr_in srv;
  struct epoll_event ev;

  int listenfd;
  int epfd;
  int clifd;
  int i;
  int res;

  char buffer[1024];
  int n;

  events = (struct epoll_event *)calloc(MAX_CLIENT, sizeof(struct epoll_event));
  
  if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
  {
    perror("error opening socket\n");
    exit(1);
  }

  bzero(&srv, sizeof(srv));
  srv.sin_family = AF_INET;
  srv.sin_addr.s_addr = INADDR_ANY;
  srv.sin_port = htons(PORT);

  if( bind(listenfd, (struct sockaddr *) &srv, sizeof(srv)) < 0)
  {
    perror("error binding to socket\n");
    exit(1);
  }
  
  listen(listenfd, 1024);

  int reuse_addr = 1;           /* Used so we can re-bind to our port */
  /* So that we can re-bind to it without TIME_WAIT problems */
  setsockopt (listenfd, SOL_SOCKET, SO_REUSEADDR, &reuse_addr,
              sizeof (reuse_addr));

  epfd = epoll_create(MAX_CLIENT);
  if(!epfd)
  {
    perror("epoll_create\n");
    exit(1);
  }
  ev.events = EPOLLIN | EPOLLERR | EPOLLHUP;
  ev.data.fd = listenfd;
  if(epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev) < 0)
  {
    perror("epoll_ctl, failed to add listenfd\n");
    exit(1);
  }


  for( ; ; )
  {
    usleep(8000);
    res = epoll_wait(epfd, events, MAX_CLIENT, 0);
    for(i = 0; i < res; i++)
    {
      if(events[i].data.fd == listenfd)
      {
        clifd = accept(listenfd, NULL, NULL);
        if(clifd > 0)
        {
          printf(".");
          nonblock(clifd);
          ev.events = EPOLLIN | EPOLLET;
          ev.data.fd = clifd;
          if(epoll_ctl(epfd, EPOLL_CTL_ADD, clifd, &ev) < 0)
          {
            perror("epoll_ctl ADD\n");
            exit(1);
          }
        }
      }
      else {
        n = recv(events[i].data.fd, buffer, 1023, 0);
        if(n == 0)  /* closed connection */
        {
          epoll_ctl(epfd, EPOLL_CTL_DEL, events[i].data.fd, NULL);
        }
        else if(n < 0)  /* error, close connection */
        {
          //epoll_ctl(epfd, EPOLL_CTL_DEL, events[i].data.fd, NULL);
      close(events[i].data.fd);
        }
        else {  /* got a request, process it. */
          //send(events[i].data.fd, buffer, n, 0);
          //bzero(&buffer, n);
          //printf("%d data received: \n%s\n", events[i].data.fd, buffer);

          //send(events[i].data.fd, response, y, 0);
      //close(events[i].data.fd);

      Respond(events[i].data.fd, buffer, n);

        }
      }
    }
  }

}

void Shutdown(){

}

int main (){

  Initialize();
  Loop();
  Shutdown();

  exit(0);
}

Seeing a lot of ads for vacuum jar sealers on facebook.

Many online advertisements and social media videos misleadingly imply that these handheld electric gadgets are a quick substitute for tradit...