Per il punto 2, chiudi lo standard output corrente (close(STDOUT_FILENO)), quindi apri il file di output (dato che la open usa il file descriptor più basso per i nuovi file descriptor, il file di output sarà automaticamente agganciato allo standard output); ripeti lo stesso gioco per il file di input (usando però STDIN_FILENO nella close).
Ora, chiamando execlp("sort", "sort", (char *)NULL);, sort si troverà lo standard input rimpiazzato dallo stream del tuo file, e lo standard output già puntato sul file di output, e procederà quindi ad ordinare il file di input, salvato nel file di output.

Per i punti 3, c'è un problema di fondo: nel momento in cui chiami la exec, il tuo codice smette di avere il controllo di alcunché, dato che stai dicendo al sistema operativo di rimpiazzare il codice del tuo programma con quello di sort; neanche gli exit handlers sopravvivono ad una exec, per cui probabilmente hai bisogno di fare una fork; il processo figlio farà quanto descritto sopra, mentre il processo padre attenderà (usando la wait) il termine del processo figlio e scriverà su standard output la frase del punto 3a; quanto al punto 3b, per registrare degli exit handler devi usare la funzione atexit.