Example #1
0
/* Try to connect to the dirmngr via socket or fork it off and work by
   pipes.  Handle the server's initial greeting */
static assuan_context_t
start_dirmngr (int only_daemon)
{
  int rc;
  char *infostr, *p;
  assuan_context_t ctx;
  int try_default = 0;

  infostr = opt.force_pipe_server? NULL : getenv ("DIRMNGR_INFO");
  if (only_daemon && (!infostr || !*infostr))
    {
      infostr = xstrdup (dirmngr_socket_name ());
      try_default = 1;
    }

  rc = assuan_new (&ctx);
  if (rc)
    {
      log_error (_("failed to allocate assuan context: %s\n"),
                 gpg_strerror (rc));
      return NULL;
    }

  if (!infostr || !*infostr)
    {
      const char *pgmname;
      const char *argv[3];
      int no_close_list[3];
      int i;

      if (only_daemon)
        {
          log_error (_("apparently no running dirmngr\n"));
          return NULL;
        }

      if (opt.verbose)
        log_info (_("no running dirmngr - starting one\n"));

      if (!opt.dirmngr_program || !*opt.dirmngr_program)
        opt.dirmngr_program = "./dirmngr";
      if ( !(pgmname = strrchr (opt.dirmngr_program, '/')))
        pgmname = opt.dirmngr_program;
      else
        pgmname++;

      argv[0] = pgmname;
      argv[1] = "--server";
      argv[2] = NULL;

      i=0;
      if (log_get_fd () != -1)
        no_close_list[i++] = assuan_fd_from_posix_fd (log_get_fd ());
      no_close_list[i++] = assuan_fd_from_posix_fd (es_fileno (es_stderr));
      no_close_list[i] = -1;

      /* Connect to the agent and perform initial handshaking.  */
      rc = assuan_pipe_connect (ctx, opt.dirmngr_program, argv,
                                no_close_list, NULL, NULL, 0);
    }
  else /* Connect to a daemon.  */
    {
      int prot;
      int pid;

      infostr = xstrdup (infostr);
      if (!try_default && *infostr)
        {
          if ( !(p = strchr (infostr, ':')) || p == infostr)
            {
              log_error (_("malformed DIRMNGR_INFO environment variable\n"));
              xfree (infostr);
              if (only_daemon)
                return NULL;
              /* Try again by starting a new instance.  */
              opt.force_pipe_server = 1;
              return start_dirmngr (0);
            }
          *p++ = 0;
          pid = atoi (p);
          while (*p && *p != ':')
            p++;
          prot = *p? atoi (p+1) : 0;
          if (prot != 1)
            {
              log_error (_("dirmngr protocol version %d is not supported\n"),
                         prot);
              xfree (infostr);
              if (only_daemon)
                return NULL;
              opt.force_pipe_server = 1;
              return start_dirmngr (0);
            }
        }
      else
        pid = -1;

      rc = assuan_socket_connect (ctx, infostr, pid, 0);
      xfree (infostr);
      if (gpg_err_code(rc) == GPG_ERR_ASS_CONNECT_FAILED && !only_daemon)
        {
          log_error (_("can't connect to the dirmngr - trying fall back\n"));
          opt.force_pipe_server = 1;
          return start_dirmngr (0);
        }
    }

  if (rc)
    {
      assuan_release (ctx);
      log_error (_("can't connect to the dirmngr: %s\n"),
                 gpg_strerror (rc));
      return NULL;
    }

  return ctx;
}
Example #2
0
/* Try to connect to the dirmngr via a socket.  On platforms
   supporting it, start it up if needed.  Returns a new assuan context
   at R_CTX or an error code. */
gpg_error_t
start_new_dirmngr (assuan_context_t *r_ctx,
                   gpg_err_source_t errsource,
                   const char *homedir,
                   const char *dirmngr_program,
                   int verbose, int debug,
                   gpg_error_t (*status_cb)(ctrl_t, int, ...),
                   ctrl_t status_cb_arg)
{
  gpg_error_t err;
  assuan_context_t ctx;
  const char *sockname;
  int did_success_msg = 0;

  *r_ctx = NULL;

  err = assuan_new (&ctx);
  if (err)
    {
      log_error ("error allocating assuan context: %s\n", gpg_strerror (err));
      return err;
    }

  sockname = dirmngr_socket_name ();
  err = assuan_socket_connect (ctx, sockname, 0, 0);
#ifdef USE_DIRMNGR_AUTO_START
  if (err)
    {
      lock_spawn_t lock;
      const char *argv[2];

      /* With no success try start a new Dirmngr.  On most systems
         this will fail because the Dirmngr is expected to be a system
         service.  However on Wince we don't distinguish users and
         thus we can start it.  A future extension might be to use the
         userv system to start the Dirmngr as a system service.  */
      if (!dirmngr_program || !*dirmngr_program)
        dirmngr_program = gnupg_module_name (GNUPG_MODULE_NAME_DIRMNGR);

      if (verbose)
        log_info (_("no running Dirmngr - starting '%s'\n"),
                  dirmngr_program);

      if (status_cb)
        status_cb (status_cb_arg, STATUS_PROGRESS,
                   "starting_dirmngr ? 0 0", NULL);

      if (fflush (NULL))
        {
          gpg_error_t tmperr = gpg_err_make (errsource,
                                             gpg_err_code_from_syserror ());
          log_error ("error flushing pending output: %s\n",
                     strerror (errno));
          assuan_release (ctx);
          return tmperr;
        }

      argv[0] = "--daemon";
      argv[1] = NULL;

      if (!(err = lock_spawning (&lock, homedir, "dirmngr", verbose))
          && assuan_socket_connect (ctx, sockname, 0, 0))
        {
          err = gnupg_spawn_process_detached (dirmngr_program, argv,NULL);
          if (err)
            log_error ("failed to start the dirmngr '%s': %s\n",
                       dirmngr_program, gpg_strerror (err));
          else
            {
              int i;

              for (i=0; i < SECS_TO_WAIT_FOR_DIRMNGR; i++)
                {
                  if (verbose)
                    log_info (_("waiting for the dirmngr "
                                "to come up ... (%ds)\n"),
                              SECS_TO_WAIT_FOR_DIRMNGR - i);
                  gnupg_sleep (1);
                  err = assuan_socket_connect (ctx, sockname, 0, 0);
                  if (!err)
                    {
                      if (verbose)
                        {
                          log_info (_("connection to the dirmngr"
                                      " established\n"));
                          did_success_msg = 1;
                        }
                      break;
                    }
                }
            }
        }

      unlock_spawning (&lock, "dirmngr");
    }
#else
  (void)homedir;
  (void)dirmngr_program;
  (void)verbose;
  (void)status_cb;
  (void)status_cb_arg;
#endif /*USE_DIRMNGR_AUTO_START*/

  if (err)
    {
      log_error ("connecting dirmngr at '%s' failed: %s\n",
                 sockname, gpg_strerror (err));
      assuan_release (ctx);
      return gpg_err_make (errsource, GPG_ERR_NO_DIRMNGR);
    }

  if (debug && !did_success_msg)
    log_debug (_("connection to the dirmngr established\n"));

  *r_ctx = ctx;
  return 0;
}
Example #3
0
/* Try to connect to the agent via socket or fork it off and work by
   pipes.  Handle the server's initial greeting.  Returns a new assuan
   context at R_CTX or an error code. */
gpg_error_t
start_new_gpg_agent (assuan_context_t *r_ctx,
                     gpg_err_source_t errsource,
                     const char *homedir,
                     const char *agent_program,
                     const char *opt_lc_ctype,
                     const char *opt_lc_messages,
                     session_env_t session_env,
                     int verbose, int debug,
                     gpg_error_t (*status_cb)(ctrl_t, int, ...),
                     ctrl_t status_cb_arg)
{
  /* If we ever failed to connect via a socket we will force the use
     of the pipe based server for the lifetime of the process.  */
  static int force_pipe_server = 0;

  gpg_error_t err = 0;
  char *infostr, *p;
  assuan_context_t ctx;
  int did_success_msg = 0;

  *r_ctx = NULL;

  err = assuan_new (&ctx);
  if (err)
    {
      log_error ("error allocating assuan context: %s\n", gpg_strerror (err));
      return err;
    }

 restart:
  infostr = force_pipe_server? NULL : getenv ("GPG_AGENT_INFO");
  if (!infostr || !*infostr)
    {
      char *sockname;
      const char *argv[3];
      pid_t pid;
      int excode;

      /* First check whether we can connect at the standard
         socket.  */
      sockname = make_filename (homedir, "S.gpg-agent", NULL);
      err = assuan_socket_connect (ctx, sockname, 0, 0);

      if (err)
        {
          /* With no success start a new server.  */
          if (!agent_program || !*agent_program)
            agent_program = gnupg_module_name (GNUPG_MODULE_NAME_AGENT);

          if (verbose)
            log_info (_("no running gpg-agent - starting '%s'\n"),
                      agent_program);

          if (status_cb)
            status_cb (status_cb_arg, STATUS_PROGRESS,
                       "starting_agent ? 0 0", NULL);

          if (fflush (NULL))
            {
              gpg_error_t tmperr = gpg_err_make (errsource,
                                                 gpg_err_code_from_syserror ());
              log_error ("error flushing pending output: %s\n",
                         strerror (errno));
              xfree (sockname);
	      assuan_release (ctx);
              return tmperr;
            }

          argv[0] = "--use-standard-socket-p";
          argv[1] = NULL;
          err = gnupg_spawn_process_fd (agent_program, argv, -1, -1, -1, &pid);
          if (err)
            log_debug ("starting '%s' for testing failed: %s\n",
                       agent_program, gpg_strerror (err));
          else if ((err = gnupg_wait_process (agent_program, pid, 1, &excode)))
            {
              if (excode == -1)
                log_debug ("running '%s' for testing failed (wait): %s\n",
                           agent_program, gpg_strerror (err));
            }
          gnupg_release_process (pid);

          if (!err && !excode)
            {
              /* If the agent has been configured for use with a
                 standard socket, an environment variable is not
                 required and thus we we can savely start the agent
                 here.  */
              lock_spawn_t lock;

              argv[0] = "--daemon";
              argv[1] = "--use-standard-socket";
              argv[2] = NULL;

              if (!(err = lock_spawning (&lock, homedir, "agent", verbose))
                  && assuan_socket_connect (ctx, sockname, 0, 0))
                {
                  err = gnupg_spawn_process_detached (agent_program, argv,NULL);
                  if (err)
                    log_error ("failed to start agent '%s': %s\n",
                               agent_program, gpg_strerror (err));
                  else
                    {
                      int i;

                      for (i=0; i < SECS_TO_WAIT_FOR_AGENT; i++)
                        {
                          if (verbose)
                            log_info (_("waiting for the agent "
                                        "to come up ... (%ds)\n"),
                                      SECS_TO_WAIT_FOR_AGENT - i);
                          gnupg_sleep (1);
                          err = assuan_socket_connect (ctx, sockname, 0, 0);
                          if (!err)
                            {
                              if (verbose)
                                {
                                  log_info (_("connection to agent "
                                              "established\n"));
                                  did_success_msg = 1;
                                }
                              break;
                            }
                        }
                    }
                }

              unlock_spawning (&lock, "agent");
            }
          else
            {
              /* If using the standard socket is not the default we
                 start the agent as a pipe server which gives us most
                 of the required features except for passphrase
                 caching etc.  */
              const char *pgmname;
              int no_close_list[3];
              int i;

              if ( !(pgmname = strrchr (agent_program, '/')))
                pgmname = agent_program;
              else
                pgmname++;

              argv[0] = pgmname;
              argv[1] = "--server";
              argv[2] = NULL;

              i=0;
              if (log_get_fd () != -1)
                no_close_list[i++] = assuan_fd_from_posix_fd (log_get_fd ());
              no_close_list[i++] = assuan_fd_from_posix_fd (fileno (stderr));
              no_close_list[i] = -1;

              /* Connect to the agent and perform initial handshaking. */
              err = assuan_pipe_connect (ctx, agent_program, argv,
                                         no_close_list, NULL, NULL, 0);
            }
        }
      xfree (sockname);
    }
  else
    {
      int prot;
      int pid;

      infostr = xstrdup (infostr);
      if ( !(p = strchr (infostr, PATHSEP_C)) || p == infostr)
        {
          log_error (_("malformed GPG_AGENT_INFO environment variable\n"));
          xfree (infostr);
          force_pipe_server = 1;
          goto restart;
        }
      *p++ = 0;
      pid = atoi (p);
      while (*p && *p != PATHSEP_C)
        p++;
      prot = *p? atoi (p+1) : 0;
      if (prot != 1)
        {
          log_error (_("gpg-agent protocol version %d is not supported\n"),
                     prot);
          xfree (infostr);
          force_pipe_server = 1;
          goto restart;
        }

      err = assuan_socket_connect (ctx, infostr, pid, 0);
      xfree (infostr);
      if (gpg_err_code (err) == GPG_ERR_ASS_CONNECT_FAILED)
        {
          log_info (_("can't connect to the agent - trying fall back\n"));
          force_pipe_server = 1;
          goto restart;
        }
    }

  if (err)
    {
      log_error ("can't connect to the agent: %s\n", gpg_strerror (err));
      assuan_release (ctx);
      return gpg_err_make (errsource, GPG_ERR_NO_AGENT);
    }

  if (debug && !did_success_msg)
    log_debug (_("connection to agent established\n"));

  err = assuan_transact (ctx, "RESET",
                        NULL, NULL, NULL, NULL, NULL, NULL);
  if (!err)
    err = send_pinentry_environment (ctx, errsource,
                                    opt_lc_ctype, opt_lc_messages,
                                    session_env);
  if (err)
    {
      assuan_release (ctx);
      return err;
    }

  *r_ctx = ctx;
  return 0;
}
Example #4
0
/* Try to connect to the agent via socket or fork it off and work by
   pipes.  Handle the server's initial greeting */
static int
start_dirmngr_ext (ctrl_t ctrl, assuan_context_t *ctx_r)
{
  int rc;
  char *infostr, *p;
  assuan_context_t ctx = NULL;
  int try_default = 0;

  if (opt.disable_dirmngr)
    return gpg_error (GPG_ERR_NO_DIRMNGR);

  if (*ctx_r)
    return 0;

  /* Note: if you change this to multiple connections, you also need
     to take care of the implicit option sending caching. */

#ifdef HAVE_W32_SYSTEM
  infostr = NULL;
  opt.prefer_system_dirmngr = 1;
#else
  infostr = force_pipe_server? NULL : getenv ("DIRMNGR_INFO");
#endif /*HAVE_W32_SYSTEM*/
  if (infostr && !*infostr)
    infostr = NULL;
  else if (infostr)
    infostr = xstrdup (infostr);

  if (opt.prefer_system_dirmngr && !force_pipe_server && !infostr)
    {
      infostr = xstrdup (dirmngr_socket_name ());
      try_default = 1;
    }

  rc = assuan_new (&ctx);
  if (rc)
    {
      log_error ("can't allocate assuan context: %s\n", gpg_strerror (rc));
      return rc;
    }

  if (!infostr)
    {
      const char *pgmname;
      const char *argv[3];
      int no_close_list[3];
      int i;

      if (!opt.dirmngr_program || !*opt.dirmngr_program)
        opt.dirmngr_program = gnupg_module_name (GNUPG_MODULE_NAME_DIRMNGR);
      if ( !(pgmname = strrchr (opt.dirmngr_program, '/')))
        pgmname = opt.dirmngr_program;
      else
        pgmname++;

      if (opt.verbose)
        log_info (_("no running dirmngr - starting `%s'\n"),
                  opt.dirmngr_program);
      
      if (fflush (NULL))
        {
          gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
          log_error ("error flushing pending output: %s\n", strerror (errno));
          return tmperr;
        }

      argv[0] = pgmname;
      argv[1] = "--server";
      argv[2] = NULL;

      i=0;
      if (log_get_fd () != -1)
        no_close_list[i++] = assuan_fd_from_posix_fd (log_get_fd ());
      no_close_list[i++] = assuan_fd_from_posix_fd (fileno (stderr));
      no_close_list[i] = -1;

      /* connect to the agent and perform initial handshaking */
      rc = assuan_pipe_connect (ctx, opt.dirmngr_program, argv,
                                no_close_list, NULL, NULL, 0);
    }
  else
    {
      int prot;
      int pid;

      if (!try_default)
        {
          if ( !(p = strchr (infostr, PATHSEP_C)) || p == infostr)
            {
              log_error (_("malformed DIRMNGR_INFO environment variable\n"));
              xfree (infostr);
              force_pipe_server = 1;
              return start_dirmngr_ext (ctrl, ctx_r);
            }
          *p++ = 0;
          pid = atoi (p);
          while (*p && *p != PATHSEP_C)
            p++;
          prot = *p? atoi (p+1) : 0;
          if (prot != 1)
            {
              log_error (_("dirmngr protocol version %d is not supported\n"),
                         prot);
              xfree (infostr);
              force_pipe_server = 1;
              return start_dirmngr_ext (ctrl, ctx_r);
            }
        }
      else
        pid = -1;

      rc = assuan_socket_connect (ctx, infostr, pid, 0);
#ifdef HAVE_W32_SYSTEM
      if (rc)
        log_debug ("connecting dirmngr at `%s' failed\n", infostr);
#endif

      xfree (infostr);
#ifndef HAVE_W32_SYSTEM
      if (gpg_err_code (rc) == GPG_ERR_ASS_CONNECT_FAILED)
        {
          log_info (_("can't connect to the dirmngr - trying fall back\n"));
          force_pipe_server = 1;
          return start_dirmngr_ext (ctrl, ctx_r);
        }
#endif /*!HAVE_W32_SYSTEM*/
    }

  prepare_dirmngr (ctrl, ctx, rc);

  if (rc)
    {
      assuan_release (ctx);
      log_error ("can't connect to the dirmngr: %s\n", gpg_strerror (rc));
      return gpg_error (GPG_ERR_NO_DIRMNGR);
    }
  *ctx_r = ctx;

  if (DBG_ASSUAN)
    log_debug ("connection to dirmngr established\n");
  return 0;
}
Example #5
0
static gpgme_error_t
uiserver_new (void **engine, const char *file_name, const char *home_dir)
{
  gpgme_error_t err = 0;
  engine_uiserver_t uiserver;
  char *dft_display = NULL;
  char dft_ttyname[64];
  char *dft_ttytype = NULL;
  char *optstr;

  uiserver = calloc (1, sizeof *uiserver);
  if (!uiserver)
    return gpg_error_from_syserror ();

  uiserver->protocol = GPGME_PROTOCOL_DEFAULT;
  uiserver->status_cb.fd = -1;
  uiserver->status_cb.dir = 1;
  uiserver->status_cb.tag = 0;
  uiserver->status_cb.data = uiserver;

  uiserver->input_cb.fd = -1;
  uiserver->input_cb.dir = 0;
  uiserver->input_cb.tag = 0;
  uiserver->input_cb.server_fd = -1;
  *uiserver->input_cb.server_fd_str = 0;
  uiserver->output_cb.fd = -1;
  uiserver->output_cb.dir = 1;
  uiserver->output_cb.tag = 0;
  uiserver->output_cb.server_fd = -1;
  *uiserver->output_cb.server_fd_str = 0;
  uiserver->message_cb.fd = -1;
  uiserver->message_cb.dir = 0;
  uiserver->message_cb.tag = 0;
  uiserver->message_cb.server_fd = -1;
  *uiserver->message_cb.server_fd_str = 0;

  uiserver->status.fnc = 0;
  uiserver->colon.fnc = 0;
  uiserver->colon.attic.line = 0;
  uiserver->colon.attic.linesize = 0;
  uiserver->colon.attic.linelen = 0;
  uiserver->colon.any = 0;

  uiserver->inline_data = NULL;

  uiserver->io_cbs.add = NULL;
  uiserver->io_cbs.add_priv = NULL;
  uiserver->io_cbs.remove = NULL;
  uiserver->io_cbs.event = NULL;
  uiserver->io_cbs.event_priv = NULL;

  err = assuan_new_ext (&uiserver->assuan_ctx, GPG_ERR_SOURCE_GPGME,
			&_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
			NULL);
  if (err)
    goto leave;
  assuan_ctx_set_system_hooks (uiserver->assuan_ctx,
			       &_gpgme_assuan_system_hooks);

  err = assuan_socket_connect (uiserver->assuan_ctx,
			       file_name ?
			       file_name : _gpgme_get_uiserver_socket_path (),
			       0, ASSUAN_SOCKET_SERVER_FDPASSING);
  if (err)
    goto leave;

  err = _gpgme_getenv ("DISPLAY", &dft_display);
  if (err)
    goto leave;
  if (dft_display)
    {
      if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
        {
	  free (dft_display);
	  err = gpg_error_from_errno (errno);
	  goto leave;
	}
      free (dft_display);

      err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL, NULL,
			     NULL, NULL, NULL);
      free (optstr);
      if (err)
	goto leave;
    }

  if (isatty (1))
    {
      int rc;

      rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
      if (rc)
	{
	  err = gpg_error_from_errno (rc);
	  goto leave;
	}
      else
	{
	  if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
	    {
	      err = gpg_error_from_errno (errno);
	      goto leave;
	    }
	  err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL, NULL,
				 NULL, NULL, NULL);
	  free (optstr);
	  if (err)
	    goto leave;

	  err = _gpgme_getenv ("TERM", &dft_ttytype);
	  if (err)
	    goto leave;
	  if (dft_ttytype)
	    {
	      if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0)
		{
		  free (dft_ttytype);
		  err = gpg_error_from_errno (errno);
		  goto leave;
		}
	      free (dft_ttytype);

	      err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL,
				     NULL, NULL, NULL, NULL);
	      free (optstr);
	      if (err)
		goto leave;
	    }
	}
    }

#ifdef HAVE_W32_SYSTEM
  /* Under Windows we need to use AllowSetForegroundWindow.  Tell
     uiserver to tell us when it needs it.  */
  if (!err)
    {
      err = assuan_transact (uiserver->assuan_ctx, "OPTION allow-pinentry-notify",
                             NULL, NULL, NULL, NULL, NULL, NULL);
      if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
        err = 0; /* This is a new feature of uiserver.  */
    }
#endif /*HAVE_W32_SYSTEM*/

 leave:
  if (err)
    uiserver_release (uiserver);
  else
    *engine = uiserver;

  return err;
}
Example #6
0
/* Fork off the SCdaemon if this has not already been done.  Lock the
   daemon and make sure that a proper context has been setup in CTRL.
   This function might also lock the daemon, which means that the
   caller must call unlock_scd after this fucntion has returned
   success and the actual Assuan transaction been done. */
static int
start_scd (ctrl_t ctrl)
{
  gpg_error_t err = 0;
  const char *pgmname;
  assuan_context_t ctx = NULL;
  const char *argv[3];
  assuan_fd_t no_close_list[3];
  int i;
  int rc;

  if (opt.disable_scdaemon)
    return gpg_error (GPG_ERR_NOT_SUPPORTED);

  /* If this is the first call for this session, setup the local data
     structure. */
  if (!ctrl->scd_local)
    {
      ctrl->scd_local = xtrycalloc (1, sizeof *ctrl->scd_local);
      if (!ctrl->scd_local)
        return gpg_error_from_syserror ();
      ctrl->scd_local->ctrl_backlink = ctrl;
      ctrl->scd_local->next_local = scd_local_list;
      scd_local_list = ctrl->scd_local;
    }


  /* Assert that the lock count is as expected. */
  if (ctrl->scd_local->locked)
    {
      log_error ("start_scd: invalid lock count (%d)\n",
                 ctrl->scd_local->locked);
      return gpg_error (GPG_ERR_INTERNAL);
    }
  ctrl->scd_local->locked++;

  if (ctrl->scd_local->ctx)
    return 0; /* Okay, the context is fine.  We used to test for an
                 alive context here and do an disconnect.  Now that we
                 have a ticker function to check for it, it is easier
                 not to check here but to let the connection run on an
                 error instead. */


  /* We need to protect the following code. */
  rc = npth_mutex_lock (&start_scd_lock);
  if (rc)
    {
      log_error ("failed to acquire the start_scd lock: %s\n",
                 strerror (rc));
      return gpg_error (GPG_ERR_INTERNAL);
    }

  /* Check whether the pipe server has already been started and in
     this case either reuse a lingering pipe connection or establish a
     new socket based one. */
  if (primary_scd_ctx && primary_scd_ctx_reusable)
    {
      ctx = primary_scd_ctx;
      primary_scd_ctx_reusable = 0;
      if (opt.verbose)
        log_info ("new connection to SCdaemon established (reusing)\n");
      goto leave;
    }

  rc = assuan_new (&ctx);
  if (rc)
    {
      log_error ("can't allocate assuan context: %s\n", gpg_strerror (rc));
      err = rc;
      goto leave;
    }

  if (socket_name)
    {
      rc = assuan_socket_connect (ctx, socket_name, 0, 0);
      if (rc)
        {
          log_error ("can't connect to socket '%s': %s\n",
                     socket_name, gpg_strerror (rc));
          err = gpg_error (GPG_ERR_NO_SCDAEMON);
          goto leave;
        }

      if (opt.verbose)
        log_info ("new connection to SCdaemon established\n");
      goto leave;
    }

  if (primary_scd_ctx)
    {
      log_info ("SCdaemon is running but won't accept further connections\n");
      err = gpg_error (GPG_ERR_NO_SCDAEMON);
      goto leave;
    }

  /* Nope, it has not been started.  Fire it up now. */
  if (opt.verbose)
    log_info ("no running SCdaemon - starting it\n");

  if (fflush (NULL))
    {
#ifndef HAVE_W32_SYSTEM
      err = gpg_error_from_syserror ();
#endif
      log_error ("error flushing pending output: %s\n", strerror (errno));
      /* At least Windows XP fails here with EBADF.  According to docs
         and Wine an fflush(NULL) is the same as _flushall.  However
         the Wime implementaion does not flush stdin,stdout and stderr
         - see above.  Lets try to ignore the error. */
#ifndef HAVE_W32_SYSTEM
      goto leave;
#endif
    }

  if (!opt.scdaemon_program || !*opt.scdaemon_program)
    opt.scdaemon_program = gnupg_module_name (GNUPG_MODULE_NAME_SCDAEMON);
  if ( !(pgmname = strrchr (opt.scdaemon_program, '/')))
    pgmname = opt.scdaemon_program;
  else
    pgmname++;

  argv[0] = pgmname;
  argv[1] = "--multi-server";
  argv[2] = NULL;

  i=0;
  if (!opt.running_detached)
    {
      if (log_get_fd () != -1)
        no_close_list[i++] = assuan_fd_from_posix_fd (log_get_fd ());
      no_close_list[i++] = assuan_fd_from_posix_fd (fileno (stderr));
    }
  no_close_list[i] = ASSUAN_INVALID_FD;

  /* Connect to the scdaemon and perform initial handshaking.  Use
     detached flag so that under Windows SCDAEMON does not show up a
     new window.  */
  rc = assuan_pipe_connect (ctx, opt.scdaemon_program, argv,
			    no_close_list, atfork_cb, NULL,
                            ASSUAN_PIPE_CONNECT_DETACHED);
  if (rc)
    {
      log_error ("can't connect to the SCdaemon: %s\n",
                 gpg_strerror (rc));
      err = gpg_error (GPG_ERR_NO_SCDAEMON);
      goto leave;
    }

  if (opt.verbose)
    log_debug ("first connection to SCdaemon established\n");


  /* Get the name of the additional socket opened by scdaemon. */
  {
    membuf_t data;
    unsigned char *databuf;
    size_t datalen;

    xfree (socket_name);
    socket_name = NULL;
    init_membuf (&data, 256);
    assuan_transact (ctx, "GETINFO socket_name",
                     membuf_data_cb, &data, NULL, NULL, NULL, NULL);

    databuf = get_membuf (&data, &datalen);
    if (databuf && datalen)
      {
        socket_name = xtrymalloc (datalen + 1);
        if (!socket_name)
          log_error ("warning: can't store socket name: %s\n",
                     strerror (errno));
        else
          {
            memcpy (socket_name, databuf, datalen);
            socket_name[datalen] = 0;
            if (DBG_IPC)
              log_debug ("additional connections at '%s'\n", socket_name);
          }
      }
    xfree (databuf);
  }

  /* Tell the scdaemon we want him to send us an event signal.  We
     don't support this for W32CE.  */
#ifndef HAVE_W32CE_SYSTEM
  if (opt.sigusr2_enabled)
    {
      char buf[100];

#ifdef HAVE_W32_SYSTEM
      snprintf (buf, sizeof buf, "OPTION event-signal=%lx",
                (unsigned long)get_agent_scd_notify_event ());
#else
      snprintf (buf, sizeof buf, "OPTION event-signal=%d", SIGUSR2);
#endif
      assuan_transact (ctx, buf, NULL, NULL, NULL, NULL, NULL, NULL);
    }
#endif /*HAVE_W32CE_SYSTEM*/

  primary_scd_ctx = ctx;
  primary_scd_ctx_reusable = 0;

 leave:
  if (err)
    {
      unlock_scd (ctrl, err);
      if (ctx)
	assuan_release (ctx);
    }
  else
    {
      ctrl->scd_local->ctx = ctx;
    }
  rc = npth_mutex_unlock (&start_scd_lock);
  if (rc)
    log_error ("failed to release the start_scd lock: %s\n", strerror (rc));
  return err;
}