[CRIU] Re: [PATCH 3/6] pstree: Allow to dump and restore session non-leaders if --shell-job passed

Pavel Emelyanov xemul at parallels.com
Wed Oct 17 06:20:20 EDT 2012


On 10/17/2012 12:32 AM, Cyrill Gorcunov wrote:
> If --shell-job passed we allow to dump and restpre session non-leaders.
> 
> This slightly changes the behaviour or the tool. If previously
> we allowed to dump non-leaders after this commit we refuse to
> do that if not explicitly requested.
> 
> Note that in sake of tty restore (which will be addressed in
> further patches) we do inherit process group for root task.
> 
> Signed-off-by: Cyrill Gorcunov <gorcunov at openvz.org>
> ---
>  include/pstree.h |    9 ++++
>  pstree.c         |  136 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
>  2 files changed, 143 insertions(+), 2 deletions(-)
> 
> 
> 0003-pstree-Allow-to-dump-and-restore-session-non-leaders.patch
> 
> 
> diff --git a/include/pstree.h b/include/pstree.h
> index 8536844..0c8c9bb 100644
> --- a/include/pstree.h
> +++ b/include/pstree.h
> @@ -9,6 +9,15 @@
>   */
>  #define INIT_PID	(1)
>  
> +/*
> + * When we need to inherit SIDs/PGIDs we put a
> + * special value in dump image.
> + */
> +#define INHERIT_SID	(-1)
> +
> +/* Some known to be invalid value */
> +#define INVALID_PID	(-2)
> +
>  struct pid {
>  	/*
>  	 * The @real pid is used to fetch tasks during dumping stage,
> diff --git a/pstree.c b/pstree.c
> index 6acf492..80c4c83 100644
> --- a/pstree.c
> +++ b/pstree.c
> @@ -69,10 +69,31 @@ int dump_pstree(struct pstree_item *root_item)
>  	int ret = -1, i;
>  	int pstree_fd;
>  
> +	pid_t origin_sid = INHERIT_SID;
> +
>  	pr_info("\n");
>  	pr_info("Dumping pstree (pid: %d)\n", root_item->pid.real);
>  	pr_info("----------------------------------------\n");
>  
> +	/*
> +	 * Make sure we're dumping session leader, if not an
> +	 * appropriate option must be passed.
> +	 *
> +	 * Also note that if we're not a session leader we
> +	 * can't get the situation where the leader sits somewhere
> +	 * deeper in process tree, thus top-level checking for
> +	 * leader is enough.
> +	 */
> +	if (root_item->pid.virt != root_item->sid) {
> +		if (!opts.shell_job) {
> +			pr_err("The root process %d is not a session leader,"
> +			       "miss option?\n", item->pid.virt);
> +			return -1;
> +		}
> +
> +		origin_sid  = root_item->sid;
> +	}
> +
>  	pstree_fd = open_image(CR_FD_PSTREE, O_DUMP);
>  	if (pstree_fd < 0)
>  		return -1;
> @@ -82,8 +103,37 @@ int dump_pstree(struct pstree_item *root_item)
>  
>  		e.pid		= item->pid.virt;
>  		e.ppid		= item->parent ? item->parent->pid.virt : 0;
> -		e.pgid		= item->pgid;
> -		e.sid		= item->sid;
> +
> +		/*
> +		 * When we're walking over all processes in the tree we
> +		 * can meet two kind of processes -- session leaders and
> +		 * group leaders, and they must remain the same on restore
> +		 * (except root item, which might inherit SID/PGID).
> +		 *
> +		 * While for SIDs iheritance it looks obvious, it's not
> +		 * same clear for GIDs. Here is an example.
> +		 *
> +		 *   PID  GID  SID
> +		 *    2    2    2    shell
> +		 *    3    2    2    `- root-task <-- dump from here
> +		 *    5    2    2      `- task-1
> +		 *    6    6    2      `- task-2
> +		 *
> +		 *   PID  GID  SID
> +		 *    1    1    1    shell
> +		 *    3    1    1    `- root-task <-- restore from here
> +		 *    5    1    1      `- task-1
> +		 *    6    6    1      `- task-2
> +		 *
> +		 * So when we migrate a shell job we need the task-1 to inherit
> +		 * GID from root task SID, thus we put INHERIT_SID in the image.
> +		 */
> +		if (item->pgid == origin_sid)
> +			e.pgid = INHERIT_SID;
> +		else
> +			e.pgid = item->pgid;
> +
> +		e.sid		= (item->sid == origin_sid) ? INHERIT_SID : item->sid;

Write in the same style.

>  		e.n_threads	= item->nr_threads;
>  
>  		e.threads = xmalloc(sizeof(e.threads[0]) * e.n_threads);
> @@ -107,12 +157,39 @@ err:
>  	return ret;
>  }
>  
> +static int try_inherit_sid(pid_t sid, PstreeEntry *e)
> +{
> +	bool modified = false;
> +
> +	if (e->sid == INHERIT_SID) {
> +		e->sid = sid;
> +		modified = true;
> +	} else if (e->pgid == INHERIT_SID) {

Why else? And why ... <see below>

> +		e->pgid = sid;
> +		modified = true;
> +	}
> +
> +	if (modified) {
> +		if (!opts.shell_job) {
> +			pr_err("Found PID %d to be inherited but "
> +			       "no option passed\n", e->pid);
> +			return -1;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
>  static int max_pid = 0;
>  int prepare_pstree(void)
>  {
>  	int ret = 0, i, ps_fd;
>  	struct pstree_item *pi, *parent = NULL;
>  
> +	pid_t current_sid = getsid(getpid());
> +	pid_t current_gid = getpgid(getpid());
> +	pid_t old_gid = INVALID_PID;
> +
>  	pr_info("Reading image tree\n");
>  
>  	ps_fd = open_image_ro(CR_FD_PSTREE);
> @@ -131,9 +208,25 @@ int prepare_pstree(void)
>  		if (pi == NULL)
>  			break;
>  
> +		/*
> +		 * Inherit SIDs/PGIDs early before building the process tree.
> +		 */
> +		ret = try_inherit_sid(current_sid, e);
> +		if (ret)
> +			goto err;
> +
> +		ret = -1;
> +
>  		pi->pid.virt = e->pid;
>  		max_pid = max((int)e->pid, max_pid);
>  
> +		/*
> +		 * Don't forget to update descending GIDs if the
> +		 * root task's GID has been altered.
> +		 */
> +		if (e->pgid == old_gid)
> +			e->pgid = current_gid;

<continued> ... is this check here, but not in the try_inherit_sid()?

> +
>  		pi->pgid = e->pgid;
>  		max_pid = max((int)e->pgid, max_pid);
>  
> @@ -148,6 +241,35 @@ int prepare_pstree(void)
>  			}
>  			root_item = pi;
>  			pi->parent = NULL;
> +
> +			/*
> +			 * Migration of a root task group leader is a bit tricky.
> +			 * When a task yields SIGSTOP, the kernel notifies the parent
> +			 * with SIGCHLD. This means when task is running in a
> +			 * shell, the shell obtains SIGCHLD and sends a task to
> +			 * the background.
> +			 *
> +			 * The situation gets changed once we restore the
> +			 * program -- our tool become an additional stub between
> +			 * the restored program and the shell. So to be able to
> +			 * notify the shell with SIGCHLD from our restored
> +			 * program -- we make the root task to inherit the
> +			 * process group from us.
> +			 *
> +			 * Not that clever solution but at least it works.
> +			 */
> +			if (pi->pid.virt == pi->pgid &&
> +			    pi->pid.virt != pi->sid) {
> +				pr_info("Migrating GID of a root process %d -> %d \n",
> +					pi->pgid, current_gid);
> +				if (!opts.shell_job) {
> +					pr_err("Found GID %d to be inherited but "
> +					       "no option passed\n", e->pgid);
> +					goto err;



> +				}
> +				old_gid = pi->pgid;
> +				pi->pgid = current_gid;
> +			}
>  		} else {
>  			/*
>  			 * Fast path -- if the pstree image is not edited, the
> @@ -205,6 +327,8 @@ int prepare_pstree_ids(void)
>  	struct pstree_item *item, *child, *helper, *tmp;
>  	LIST_HEAD(helpers);
>  
> +	pid_t current_pgid = getpgid(getpid());
> +
>  	/*
>  	 * Some task can be reparented to init. A helper task should be added
>  	 * for restoring sid of such tasks. The helper tasks will be exited
> @@ -331,6 +455,14 @@ int prepare_pstree_ids(void)
>  		if (gleader)
>  			continue;
>  
> +		/*
> +		 * If the PGID is eq to current one -- this
> +		 * means we're inheriting group from the current
> +		 * task so we need to escape creating a helper here.
> +		 */
> +		if (current_pgid == item->pgid)
> +			continue;

Isn't it handled by gleader check above? Why?

> +
>  		helper = alloc_pstree_item();
>  		if (helper == NULL)
>  			return -1;
> 




More information about the CRIU mailing list