First of all: setsid() will make your process a process group leader, but it will also make you the leader of a new session. If you are just interested in getting your own process group, then use setpgid(0,0).
Now for understanding the actual reason why setsid() returns EPERM if you already are process group leader or session leader you have to understand that process group and session ids are initialized from the process id of the process creating them (and hence leading them, i.e. for a session leader pid == sid and for a process group leader pid == pgid). Also process groups cannot move between sessions.
That means if you are a process group leader, and creating a new session would be allowed then the sid and the pgid would get set to your pid, leaving the other processes in your old process group in a weird state: their process group leader suddenly is in a different session then they themselves might be. And that cannot be allowed, hence the EPERM by the kernel.
Now if you fork() once you are neither session nor process group leader anymore and hence setting your sid and pgid to your pid is safe, because there are no other processes in such group.
So, yepp, think about it, it all makes sense.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…