Remote shutdown with C++ on Linux

So, carrying on from my Setting up Wake-on-lan on Ubuntu Server 18.04 LTS article, I decided that I would probably like to shut down my server a little bit more easily too.

In case you’re not sure what server I’m referring to – I run a local headless ubuntu server at home to allow me to work on database driven projects across multiple devices without having to keep local copies of the data on each device. It has your standard LAMP stack on there, nothing too fancy.

Until recently, I was logging in via SSH and running sudo poweroff to shut down the system, not terribly convenient. I figured, if I’m running a python script from terminal to fire up my server, I should probably get another system implemented to run another thing from terminal to shut it down, and so I did.

It’s been quite a while since I’ve written anything meaningful in C++. In fact, the last piece of C++ code I wrote with a purpose was in 2012 when my laptop J key was broke – it kept sending keystrokes without being pushed. So I threw together a low level keyboard hook to discard the key and listen for the period key instead, which it would then replace with a J. Not very convenient but it was a good temporary solution at the time.

Anyway, I digress. So, I could probably have done this in Python but I’m not a Python programmer, I’ve barely even studied it. Shocking, right? I can read it, sure, but not write. So, C++ was a nice alternative since I didn’t want to go through the hassle of installing java on my server for the sake of one task, all that extra JVM overhead was burning my soul. That and my linux distro has C/C++ compilers installed already so no extra work needed there.

 

The Client-Server Model

Everyone with even a slight understanding of the internet is familiar with the basics of a client-server model. The server listens, the client hollers and the server responds. Without getting into the details of protocols, packets and such, that’s the crux of it. With that in mind, it gave me a good place to start.

I was going to need a server, on my server, to listen. That sounds crazy, right? ambiguity at its finest!

What I mean is, I needed a program on my server machine to listen on a particular port for a particular piece of data to trigger a shutdown, I decided to call it the shutdown server since that would be its sole purpose and it describes its purpose perfectly.

The Server

So I’m just going to dump the code here and let you have a read before I explain anything about it:

#include <unistd.h>
#include <iostream>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <string.h>
#include <linux/reboot.h>
#include <sys/reboot.h>

int main(int argc, char const* argv[]) {
	const int port = 8000;

	bool shutdown = false;
	struct sockaddr_in address;
        int new_socket, input;
        int opt = 1;
	int addrlen = sizeof(address);
	char *response = "shutdown command received";

	int sockfd = socket(AF_INET, SOCK_STREAM, 0);

	if(sockfd < 0) {
		std::cout << "Socket Creation Failed" << std::endl;
		exit(-1);
	}

	if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)) < 0) {
		std::cout << "setsockopt failed";
		exit(-1);
	}	

	address.sin_family = AF_INET;
	address.sin_addr.s_addr = INADDR_ANY;
	address.sin_port = htons(port);

	if(bind(sockfd, (struct sockaddr *) &address, sizeof(address))<0) {
		std::cout << "Bind failed" << std::endl;
		exit(-1);
	}

	if(listen(sockfd, 3) < 0){
		std::cout << "Listen failed" << std::endl;
		exit(-1);
	}

	while(1) {
		char buffer[1024] = {0};

	       	if ((new_socket = accept(sockfd, (struct sockaddr*) &address, (socklen_t*)&addrlen))<0) {
                std::cout << "accept failed" << std::endl;
                exit(-1);
		    }
	
		input = read( new_socket , buffer, 1024);
		if(input <= 0) { break;}

		if(strcmp(buffer, "shutdown -local") == 0) {
			send(new_socket, response, strlen(response), 0);

			shutdown = true;
			close(new_socket);
			break;
		} else {
			send(new_socket, "hello", strlen("hello"),0);
			close(new_socket);
		}
	
	}	

	if(shutdown) {
		std::cout << "Remote shutdown requested" << std::endl;
		reboot(LINUX_REBOOT_CMD_POWER_OFF);
	}
	return 0;
}

First of all, we need to create a socket file descriptor to use:

int sockfd = socket(AF_INET, SOCK_STREAM, 0);

if sockfd contains a negative integer, something went wrong. So we check that with an if statement before going any further.

Sidenote: This is a very crude program so it’ll simply quit if an error is encountered

The next section of code relating to setsockopt() doesn’t strictly have to be used, it attempts to force the program to use the defined port. Since my machine is absolutely not running anything on port 8000, I’m happy to make use of the function.

Following, we populate the sockaddr_in struct with some required values (tell it to use IPv4, accept connections from any address and provide the port to listen on).

Sidenote: I’m not worried about accepting connections from any address because my server is configured to listen on LAN only and it sits behind two firewalled routers and runs its own firewall too

The next step is to attempt to bind the socket file descriptor to the given port with the pre-configured struct data, using bind()

bind(sockfd, (struct sockaddr *) &address, sizeof(address)

and exit if it fails. If it’s successful, however, we will attempt to listen(sockfd, 3) for new connections and enter an infinite loop.

The infinite loop is required to continually accept incoming connections and respond accordingly. There are only two responses here. The first will shut down the system if the data “shutdown -local” is received, the other will simply reply with “hello” and then close the connection and await a new one.

If the accept() function is called before the loop, it will only ever accept one connection and never wait for a subsequent connections.

The program is running on linux, so glibc is used to access reboot(LINUX_REBOOT_CMD_POWER_OFF); which will tell the system to power off. This actually requires sufficient permissions to work but I added the compiled program to systemd to run as a service, since I’ll want the shutdown server to be listening automatically as soon as the server is powered on. I’ll explain this a bit more shortly. The service runs with sufficient permissions; otherwise, if you’re testing the code, you’ll probably need to run it as root.

g++ was used to compile.

The Client

The client program will primarily be running from my MacBook so it was compiled on there with g++. It is considerably shorter:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <iostream>

   
int main(int argc, char const *argv[]) 
{ 
    const int port = 8000;
    struct sockaddr_in address; 
    int sock = 0, response;
    struct sockaddr_in serv_addr; 
    char *data = "shutdown -local";
    char buffer[1024] = {0}; 
    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    { 
        std::cout << "socket creation error" << std::endl;
        return -1; 
    } 
   
    memset(&serv_addr, '0', sizeof(serv_addr)); 
   
    serv_addr.sin_family = AF_INET; 
    serv_addr.sin_port = htons(port); 
       
    if(inet_pton(AF_INET, "local.ip.here", &serv_addr.sin_addr)<=0)
    { 
        std::cout << "invalid address" << std::endl;
        return -1; 
    } 
   
    if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
    { 
        std::cout << "connection failed" << std::endl;
        return -1; 
    } 
    send(sock, data, strlen(data), 0);
    response = read( sock , buffer, 1024);
    printf("%s\n",buffer ); 
    return 0; 
} 

A quick client summary

Similarly to the server, we define our variables and create a socket. If that’s successful, we validate the given IP address and attempt to connect. Providing there are no errors, we send the data to the server. In this case the data is simply a string containing the phrase “shutdown -local” which is a specific string that the shutdown server is listening out for. The client then waits for a response and then displays it before exiting gracefully by returning zero.

This client source code was compiled on my MacBook and placed in a directory that is added to PATH dedicated to custom scripts and programs that I might like to run from terminal, so I don’t have to navigate anywhere or type full path names when I open terminal.

Compiled with: g++ client.cpp -o sds

and placed in the above mentioned directory, I can simply open terminal on my MacBook and type “sds” to shutdown my server. This coupled with the “wol.py script to wake the server means that I can turn it on and off with absolute ease, remotely.

Creating a Service to automatically run the shutdown server on star-up

In the same way as detailed in Setting up Wake-on-lan on Ubuntu Server 18.04 LTS, I created a service to run my shutdown server automatically too.

The service is incredibly basic. I created a file called sdserv.service in /etc/systemd/system which contains the following:

[Unit]
Description=Listen for local shutdown command

[Service]
ExecStart=/home/tim/cpp/sdserver

[Install]
WantedBy=multi-user.target


The file located at /home/tim/cpp/sdserver is the binary of the compiled server source code above. ExecStart requires an absolute file path.

I then told systemd to refresh its cache of services with systemctl daemon-reload

systemctl enable sdserv.service tells systemd to run the service on start-up and systemctl start sdserv.service starts the service.

and thats it! After putting all that together, I can now run wol.py to turn on my server and sds to turn it off!

MySql Access Denied for User root without sudo

ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO) is not something you want to see when you try and login to mysql using the default root account with no password, especially when there is no password, on a fresh install!

So this was the problem I faced when I installed MySql on Ubuntu Server 18.04 TLS.

I SSH into my server since its a headless machine, just sat there with no peripherals connected at all. Just an ethernet cable and power cable. I was beginning to set up the machine as a local network server for a database heavy project I’m working on, so I installed MySql with sudo apt install mysql-server and immediately tried logging in to create a non-root user for my tasks:

mysql -u root

I typed. It threw me that error above. So I tried it with sudo and sure enough it worked. I’m not entirely sure why, but it did. Perhaps because my machine is referred to as the-lab and MySql just didn’t know who that was, because it isn’t localhost. Who knows.

Anyway, to remedy this particular issue I had to run a series of commands that removed the root user and added it again, after logging in with sudo. The steps were as follows, in case you find yourself in a similarly annoying situation:

  1. sudo mysql -u root
  2. drop user 'root'@'localhost'; This one is a bit scary because, well, you’re deleting the root user. But rest assured you can still continue to run commands after this, I promise.
  3. create user 'root'@'localhost' identified by '';
  4. grant all privileges on *.* to 'root'@'localhost' with grant option;
  5. flush privileges;

Now exitMySql with exit; or CTRL+D and try to log in again without sudo, job done!

Setting up Wake-on-lan on Ubuntu Server 18.04 LTS

Alright, where to begin?!

When I work from home, which is almost always, I often find myself working on various devices (MacBook, Dell laptop, desktop) and lately I’ve been working on a heavily database oriented project. Local dev with databases involved isn’t very friendly across multiple machines. You can add your code to VCS but including a database full of test data isn’t my idea of a fun time.

So, long story short, I re-purposed my linux desktop pc from its semi-neglected state to something more useful. A LAN MySql server. Of course, there’s the whole LAMP stack on there just for shigs, though. That, and ease of data management with PMA.

So what is the problem?

Well, I’m lazy. I’m a developer. I’m not going to go to where my server is located and turn it on every time I need to access it, no sir! Nor am I going to leave it on permanently, leeching my hard earned electricity from under my nose. That coupled with the fact that it’s next to my bed and when night sets in, it sounds like an engine next to my face. So, I need to be able to turn it on from my laptops. Easy stuff, wake-on-lan at your service.

The BIOS supports it, awesome, why not. So, I set the bios to listen for those magic packets and locate a tutorial to enable Ubuntu to cooperate. If you’re unaware, generally speaking, when an Operating System shuts down it also powers down the network adapter. This is bad for Wake-on-lan. If it’s not on, it’s not listening! The problem is, all the tutorials I’m finding are for Ubuntu pre-18, at which point Ubuntu dropped Upstart support.



The solution

The steps for preparing Ubuntu for WoL are as follows:

  1. Install ethtool with:
    sudo apt-get install ethtool
    – Ubuntu Server 18.04 LTS already has this installed
  2. Run:ifconfig
    to get the name of your network interface. Its worth noting here that all the guides I found say that “your network interface is most likely eth0”. The thing is, that’s no longer the case. Ubuntu has been transitioning to systemd since version 15.04 and part of that transition is the implementation of Predictable Network Interface Naming and so you might just find that your network interface name is something along the lines of:enp0s15
    Regardless, run: ifconfig
    to get your network interface name: 
  3. Run the command sudo ethtool -s interface wol g
    and this tells ethtool to tell the network card to listen out for magic packets. The g denotes that it should listen for magic packets. The problem is, this solution isn’t persistent between shutdowns so once the machine is powered down and then booted up again, the setting is lost. The general consensus is that you should create a system service that runs this command at start-up.
  4. On Ubuntu 18.04, you need to create a systemd service as opposed to enabling, creating and/or modifying rc.local as you would’ve done on previous versions. So, navigate to
    /etc/systemd/system
    and create a new file here called `wol.service` – you could be more descriptive but I prefer short filenames, I know what wol means here. I use vim for all my terminal based editing so I run this command: sudo vim wol.service
    to create and begin editing my service file.
  5. Now you need to populate your wol.service file with all it needs to run as a service. The most comprehensive documentation I found on this was provided by RedHat linux here. My file looks like this:
    [Unit]
    Description=Configure Wake-up on LAN
    
    [Service]
    Type=oneshot
    ExecStart=/sbin/ethtool -s enp35s0 wol g
    
    [Install]
    WantedBy=basic.target
    

    ExecStart provides the command to run. It’s important to note that this must be the absolute path to ethtool because services don’d do relative paths. Check the documentation if you want to understand the file structure more thoroughly.

  6. Once you’ve created your file, you need to add it to the systemd services so you should run systemctl daemon-reload to tell the system to update and/or locate new service files, like the one we just created.
  7. Run systemctl enable wol.service to enable the service to run on start up and,
  8. finally, systemctl start wol.service to fire up the service. This may be a redundant command but I’m not sure if step 7 does this automatically or not so there’s no harm in running it anyway.

And there we have it, if you’ve gone through all of that and enabled Wake-on-lan in BIOS, you should be able to power off your machine and then wake it up with a magic packet.


The Magic Packet

I opted to use a python script to send my magic packet, provided by San Bergmans, thank you! I had to modify it ever so slightly, maybe because I’m using it on a Mac, it expected a mac address as a parameter and it always took its own filename as the first parameter, which is obviously not a mac address, regardless of whether or not I actually provided it a parameter. I actually wen’t further than that and just dumped my mac address in file, skipped the parameter requirement and now it just sends a magic packet with my pre-defined mac address whenever I run the script. I literally type:
wol.py
in my mac terminal and the magic packet is sent, the server fires up and I’m good to go! (I have the script in a directory added to path, some advise against such a practice but convenience is what I thirst for!)

Xcode, ffmpeg and mac

So for the last 2 hours I’ve been getting errors trying to compile some C++ code on my mac that makes use of ffmpeg. For clarity, I’m using a modern MBP, Xcode and C++ to try and compile some simple ffmpeg code to spit out a list of stuff I can’t even remember the reason for right now.

...Undefined symbols for architecture x86_64:
"avcodec_register_all()...

This error was telling me that the linker can’t find a particular library required for compiling. So I tried rebuilding ffmpeg a few times with different options, I tried linking the include and lib directories to Xcode a couple of different ways, I tried changing the code several times too, I even tried compiling via terminal with g++, all with no luck.

So, tired as hell, I give it one more try. I realise that, thanks to some crazy Chinese forum with snippets of English on it, ffmpeg uses C, not C++. So I had to change my header includes to reflect that.


#ifdef __cplusplus
extern "C" {
#include "libavformat/avformat.h"
}
#endif

instead of:

#include <libavformat/avformat.h>

An unbelievably simple fix to a nightmare-ish problem, such is the life of a programmer.

So, I don’t write a lot of stuff here anymore but decided to put this up just in case someone else finds themselves in the same situation in the distant future.

Timing Code Execution in Java

Here’s a method for calculating the nth Fibonacci number, written in Java.

	static int fibN(int n) {
		if(n <= 1) {
			return n;
		}
		else {
			return fibN(n-1) + fibN(n-2);
		}
	}

This method is great for demonstrating recursion but it's terribly inefficient for the task at hand. A more efficient method of computing the nth Fibonacci sequence would be to literally count from the first to the nth sequence number. To demonstrate this, I'll be timing the code execution of both the method above, fibN(), and the method below, fibN2().

	static int fibN2(int n) {
		int current 	= 1;
		int previous 	= 0;
		int next 	= 1;
		int temp 	= 0;

		for(int i = 1; i < n; i++ ) {

			temp = current;
			next = previous+current;
			current = next;
			previous = temp;
		}

		return next;
	}

I'll be using Java's System.nanoTime() to time this code. Each method will be called from within a loop 45 times, the nanoTime() being noted before the loop starts and when the loop ends in order to calculate execution time. Remember, end time minus start time equals duration. So, here's the full class for this process:

class Fib {
	
	static long startTime 	= 0;
	static long endTime 	= 0;
	static long duration	= 0;
	static long duration2	= 0;

	public static void main(String[] args) {
		System.out.println("Recursive: ");

		startTime = System.nanoTime();
		for(int i=1; i <= 45; i++)
			System.out.println(fibN(i));

		endTime = System.nanoTime();
		duration = (endTime - startTime);

		System.out.println("execution took " + duration + " nanoseconds");

		System.out.println("Non-Recursive: ");

		startTime = System.nanoTime();
		for(int i=1; i <= 45; i++)
			System.out.println(fibN2(i));

		endTime = System.nanoTime();
		duration2 = (endTime - startTime);

		System.out.println("execution took " + duration2 + " nanoseconds");

		System.out.println(duration + " vs \n" + duration2);
	}

	static int fibN(int n) {
		if(n <= 1) {
			return n;
		}
		else {
			return fibN(n-1) + fibN(n-2);
		}
	}

	static int fibN2(int n) {
		int current 	= 1;
		int previous 	= 0;
		int next 	= 1;
		int temp 	= 0;

		for(int i = 1; i < n; i++ ) {

			temp = current;
			next = previous+current;
			current = next;
			previous = temp;
		}

		return next;
	}
}

timing

This image shows the result of this code. As you can see, fibN2() is considerably faster than fibN(), thus proving that it's much more efficient to count up to the nth Fib number than it is to use recursion to find the nth number.

A final note, nanoseconds are pretty rubbish to work with to be honest. At least, in this example. So change
System.nanoTime()
to
System.currentTimeMillis()

instead to time the processes in milliseconds. This will give a more friendly result.

Merging Shift and Vernam Cipher

This is the outcome of my attempt of merging the Shift and Vernam ciphers as part of the Computer Security module of my B.Sc. Computer Science degree, third year. The task itself is designed to stimulate an interest in a wide range of security topics. One of the following tasks were to be chosen:

1. Design and implement a secure password system

2. Analyse an existing password program

3. Improve the security level of an existing password system

The task was primarily aimed a group collaboration, teams consisting of 3 people only. Alice, Bob and Charlie (A,B and C).

Person A would be responsible for the planning, schedule and preliminary research for the password system.

Person B would be responsible for the implementation of the password system.

Person C would be responsible for the security quality of the password system.

The quotes above are taken directly from the coursework specification and it appears that the responsibilities of each person don’t make much sense in regards to tasks 2 and 3.

 

I opted to do this task solo because, well, it was permitted for starters and although group work is good for building teamwork skills, its not really fair for an official grade to be given to one person based on somebody else’s, positive or negative.

So the idea behind the system I attempted to create was to merge the functionality of the Shift and vernam ciphers. By merging them, I mean performing one and then the other consecutively. The data to be encrypted was a password as plaintext which would be part of a user account administration software for a local area network, totally fictional of course. The report discusses what I was attempting to achieve, when I noticed that things weren’t going according to plan and why and how I completed the overall system with some last minute changes due to the problem discovered.

Anyway, the report is attached – I just wanted to share it with the world lol 😀

 

Merging Shift and Vernam Cipher

Creating jar file with MySQL JDBC Connector

So after trying to make a simpler method of executing some Java code that depends on MySQL JDBC drivers via CLI I came up with this solution. It basically involves 3 stages:

  1. Extract contents of MySQL JDBC connector (mysql-connector-java-5.1.28-bin.jar in this example)
  2. Create a manifest file and compile your Java files
  3. Package them all up nice and neatly into your very own jar

These steps assume you are in the correct directory with the required file(s) present when executing commands.

1. To extract the contents of the mysql-connector-java-5.1.28-bin.jar file you execute the following command:-

jar xf mysql-connector-java-5.1.28-bin.jar

This will put all the contents of the jar file in the current directory. This includes 3 directories (META-INFcom and org)

2. Create a manifest.txt file and compile your Java file(s).

The manifest just needs to include the main class of your program for this objective and will be written like so: ‘Main-Class: mainClass’ where you replace mainClass with the class name of your program containing the main() method (if you have a Java file called Dinosaur.java, you replace mainClass with Dinosaur).

a. Create the manifest file:-

echo Main-Class: Dinosaur>manifest.txt

b. Compile your Java file(s)

javac *.java

3. Finally, you need to put it all back together again in one big box …or jar

jar cvfm jarName.jar manifest.txt *.class META-INF com org

This will add all of the contents extracted from the MySQL JDBC Driver, your manifest information and any class files that were created via stage 2b.

To run this .jar file, use this command:-

java -jar jarName.jar

The code I am using for database connection testing and jar creation:-
import java.sql.*;
class className
{
public static void main(String[] args) throws Exception {
 Connection con = null;
 Statement st = null;
 ResultSet rs = null;
 int numberOfColumns=6;
 String url = "jdbc:mysql://host/database";
 String user = "username";
 String password = "password";
try {
 con = DriverManager.getConnection(url, user, password);
 st = con.createStatement();
 rs = st.executeQuery("SELECT * FROM table");
while (rs.next()) {
 for(int i=1; i<numberOfColumns;i++)
 {
 System.out.print(rs.getString(i) + " "); 
 }
 }
} catch (SQLException ex) {
 System.out.println(ex);
 } finally {
 try {
 if (rs != null) {
 rs.close();
 }
 if (st != null) {
 st.close();
 }
 if (con != null) {
 con.close();
 }
} catch (SQLException ex) {
 System.out.println(ex);
 }
 }
 }
}

Count your lucky stars!

Okay, ignore the stars… this is a classic ‘dodgy but works’ get the job done kind of scenario where I had to modify a program last year to compare words from file X to words in file Y on a character by character basis to verify spelling – a crude spell checker, if you will.

 

Anyway, edited the code to count braces ), {, ( and }, in a given source file -> useful if your code wont compile and you want to make sure there’s the right number of brackets and braces present. Of course, strings may include such braces and throw an offset to your result but regardless, here the source and image be:-

Search.java Example

Search.java Example

//imports
import java.io.*;
import java.util.*;

//class
public class search
{
  public int n,j,k,l;
  private ArrayList<String> fc = new ArrayList<String>(); //file content

  //main 
  public static void main(String[] argz)
  {

    search s = new search(); //instance of class
	s.n=0;
	s.j=0;
	s.k=0;
	s.l=0;
    s.loadFile(); //load file content to ArrayList
    s.results();
  }

  //ask user for path to file and load contents word by word into arraylist
  public void loadFile()
  {
    System.out.println("Enter path to text file for brace counting"); //ask for file path
    Scanner scan = new Scanner(System.in); //create scanner object to obtain file path
    String filename=scan.next(); //get file path and store in string var
    File loadedFile=null; //create null File var
    try
    {
      loadedFile = new File(filename); //instantiate File obj using given file path
      scan = new Scanner(loadedFile); //instantiate new Scanner object using File object
    }
    catch(FileNotFoundException e) //handle file not found exception
    {
      //notify user that file not found and ...
      System.out.println("File '"+filename+"' not found.");
      //...exit
      System.exit(0);
    }

    String newword=""; //string var to hold each word

    while (scan.hasNextLine ()) //while next line is available ...
    {

      String line =scan.nextLine(); //...get next line from text file

      //for the length of the line, go through each character to locate next word in line
      //and store word in the newword var for adding to the file content arraylist
      for (int i=0;i<line.length();i++)
      {
	//if character does not equal , ; : ? . [whitespace] [line-break] [carriage-return]... add char to newword var...
        if (line.charAt(i) == '(')
        {
		n++;

        }
        if (line.charAt(i) == ')')
        {
		j++;

        }
        if (line.charAt(i) == '{')
        {
		k++;

        }
        if (line.charAt(i) == '}')
        {
		l++;

        }
        else //...else store word in file content arraylist and set newword var to "" aka empty string value
        {

        }
      }

    }
  }

private void results()
{
System.out.println("( found: " + n);
System.out.println(") found: " + j);
System.out.println("{ found: " + k);
System.out.println("} found: " + l);
}
}

A day in the life of php

So, I had today off (saaaame as every Tuesday!)  What better way to spend it than doing 10 weeks worth of lab tasks for Databases, Networks and the web. That’s that out the way.

Despite the fact that it was a whole term’s worth of exercises, they were quite simple for someone who’s been using PHP for the best part of 15 years. Reallly simple. Posting form data and spamming superglobals, loops in HTML forms (.php files, obviously), creating and eating (deleting) cookies, cookie based CSS, arrays, MySQL communication etc etc.

My next task at hand is to do the coursework for that course which, judging by the requirements, should only take a day or 2 at ze most. That will involve creating a database and web based CD sales site.

The only downside to this is all of our lecturer’s example code seems hella outdated. Using “print” instead of “echo”, not that there’s anything wrong with that, print is just weird and always returns 1 and can only print one string which makes no sense for something that isn’t a method but kind of looks like a method if you want it to. Using mysql_ functions when the PHP.net docs clearly, by clearly i mean big ass pink box at top of documentation screaming READ ME, states the method is deprecated and will be removed in future revisions of PHP.  It’s not the end of the world, just less fortunate for anyone who is unaware that the relative content is outdated 🙁

 

Anyway, plan for the term -> do everything in the first few weeks and waste my time looking for a job! Money bitches, that’s what we needin’

 

1) Dynamic web coursework x2 (60/40)

2) Internet and distributed Programming labs and coursework

3) PAP coursework

4) Social Computing coursework.