This is freeing all the nodes in the list, and also what they point to from their a members (but not the b members).
The recursive calls first step through the list nodes until it reaches a node whose head->next element is NULL.
Within each recursive call, head points to the current element. After the recursive call returns, it frees what head->a points to and then frees the current element with free(head);.
The test if (head->next) is redundant, since free_list() checks whether it's called on a null pointer with if (head).
Most people write this kind of loop iteratively rather than recursively, as you could get a stack overflow when trying to free a really long list.
while (head) {
free(head->a);
listint_s *temp = head;
head = head->next;
free(temp);
}