xpra icon
Bug tracker and wiki

This bug tracker and wiki are being discontinued
please use https://github.com/Xpra-org/xpra instead.


Ticket #172: 0001-enable-xpra-to-select-display-automaticaly-v3.patch

File 0001-enable-xpra-to-select-display-automaticaly-v3.patch, 11.0 KB (added by Benoit Gschwind, 7 years ago)
  • src/xpra/scripts/main.py

    From 96cb41b47e86c1b79cdd6626c9b00454f2f17d00 Mon Sep 17 00:00:00 2001
    From: Benoit Gschwind <gschwind@gnu-log.net>
    Date: Thu, 29 May 2014 16:49:55 +0200
    Subject: [PATCH] enable xpra to select display automaticaly v3
    
    ---
     src/xpra/scripts/main.py   |   1 -
     src/xpra/scripts/server.py | 160 ++++++++++++++++++++++++++++++++-------------
     2 files changed, 115 insertions(+), 46 deletions(-)
    
    diff --git a/src/xpra/scripts/main.py b/src/xpra/scripts/main.py
    index 1f6ea8a..d575ccc 100755
    a b from xpra.scripts.config import OPTION_TYPES, ENCRYPTION_CIPHERS, \ 
    3030SOCKET_TIMEOUT = int(os.environ.get("XPRA_SOCKET_TIMEOUT", 10))
    3131TCP_NODELAY = int(os.environ.get("XPRA_TCP_NODELAY", "1"))
    3232
    33 
    3433def enabled_str(v):
    3534    if v:
    3635        return "enabled"
  • src/xpra/scripts/server.py

    diff --git a/src/xpra/scripts/server.py b/src/xpra/scripts/server.py
    index 9649ee1..66b800b 100644
    a b import atexit 
    1717import signal
    1818import socket
    1919import getpass
     20import select
     21import re
    2022
    2123from xpra.scripts.main import TCP_NODELAY
    2224from xpra.dotxpra import DotXpra, ServerSockInUse
    def write_runner_shell_script(dotxpra, contents): 
    225227
    226228
    227229def display_name_check(display_name):
    228     if display_name.startswith(":"):
    229         n = display_name[1:]
    230         p = n.find(".")
    231         if p>0:
    232             n = n[:p]
     230    if re.match("^:\d+(\.\d+)?$", display_name):
     231        n = re.search("\d+", display_name).group(0)
    233232        try:
    234233            dno = int(n)
    235234            if dno>=0 and dno<10:
    def close_all_fds(exceptions=[]): 
    363362            return
    364363    print("Uh-oh, can't close fds, please port me to your system...")
    365364
    366 def open_log_file(dotxpra, log_file, display_name):
    367     if log_file:
     365# Create a temporary file while we doesn't know the display_name
     366def open_log_file(logpath):
     367    save_old_log_file(logpath)
     368    return os.open(logpath, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, o0666)
     369
     370# move the log file to his final location
     371def select_log_file(dotxpra, log_file, display_name):
     372    if log_file and display_name:
    368373        if os.path.isabs(log_file):
    369374            logpath = log_file
    370375        else:
    371376            logpath = os.path.join(dotxpra.sockdir(), log_file)
    372377        logpath = logpath.replace("$DISPLAY", display_name)
    373     else:
     378    elif display_name:
    374379        logpath = dotxpra.log_path(display_name) + ".log"
    375     sys.stderr.write("Entering daemon mode; "
     380        sys.stderr.write("Entering daemon mode; "
    376381                     + "any further errors will be reported to:\n"
    377382                     + ("  %s\n" % logpath))
     383    else:
     384        logpath = dotxpra.sockdir() + "/tmp_%d.log" % os.getpid()
     385    return logpath
     386
     387def save_old_log_file(logpath):
    378388    # Do some work up front, so any errors don't get lost.
    379389    if os.path.exists(logpath):
    380390        os.rename(logpath, logpath + ".old")
    381     return os.open(logpath, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, o0666)
    382391
    383392def daemonize(logfd):
    384393    os.chdir("/")
    def daemonize(logfd): 
    387396    os.setsid()
    388397    if os.fork():
    389398        os._exit(0)
    390     close_all_fds(exceptions=[logfd])
     399    # save current stdout/stderr to be able to print info ot user before exiting definitivly
     400    old_fd_stdout = os.dup(1)
     401    old_fd_stderr = os.dup(2)
     402    close_all_fds(exceptions=[logfd,old_fd_stdout,old_fd_stderr])
    391403    fd0 = os.open("/dev/null", os.O_RDONLY)
    392404    if fd0 != 0:
    393405        os.dup2(fd0, 0)
    394406        os.close(fd0)
     407    # reopen STDIO files
     408    old_stdout = os.fdopen(old_fd_stdout, "w", 1)
     409    old_stderr = os.fdopen(old_fd_stderr, "w", 1)
     410    # replace standard stdout/stderr by the log file
    395411    os.dup2(logfd, 1)
    396412    os.dup2(logfd, 2)
    397413    os.close(logfd)
    398414    # Make these line-buffered:
    399415    sys.stdout = os.fdopen(1, "w", 1)
    400416    sys.stderr = os.fdopen(2, "w", 1)
     417    return (old_stdout, old_stderr)
     418   
    401419
    402420
    403421def sanitize_env():
    def start_Xvfb(xvfb_str, display_name): 
    464482            sys.stderr.write("Error trying to create XAUTHORITY file %s: %s\n" % (xauthority, e))
    465483    subs = {"XAUTHORITY"    : xauthority,
    466484            "USER"          : os.environ.get("USER", "unknown-user"),
    467             "HOME"          : os.environ.get("HOME", os.getcwd()),
    468             "DISPLAY"       : display_name}
     485            "HOME"          : os.environ.get("HOME", os.getcwd()) }
     486    if display_name:
     487        subs["DISPLAY"] = display_name
    469488    for var,value in subs.items():
    470489        xvfb_str = xvfb_str.replace("$%s" % var, value)
    471490        xvfb_str = xvfb_str.replace("${%s}" % var, value)
    472     xvfb_cmd = xvfb_str.split()+[display_name]
    473     xvfb_executable = xvfb_cmd[0]
    474     xvfb_cmd[0] = "%s-for-Xpra-%s" % (xvfb_executable, display_name)
     491
    475492    def setsid():
    476493        #run in a new session
    477494        if os.name=="posix":
    478495            os.setsid()
    479     xvfb = subprocess.Popen(xvfb_cmd, executable=xvfb_executable, close_fds=True,
     496
     497    # TODO: if Xvfb does not support -displayfd -> Error
     498    if not display_name:
     499        # allocate display automaticaly
     500        r_pipe, w_pipe = os.pipe()
     501        xvfb_cmd = xvfb_str.split()+["-displayfd", str(w_pipe)]
     502        xvfb_executable = xvfb_cmd[0]
     503        xvfb_cmd[0] = "%s-for-Xpra-%s" % (xvfb_executable, str(os.getpid()))
     504        xvfb = subprocess.Popen(xvfb_cmd, executable=xvfb_executable, close_fds=False,
     505                                stdin=subprocess.PIPE, preexec_fn=setsid)
     506        # Wait to get display number from Xvfb
     507        buf = ""
     508        # Wait at most 3 time 4.0 seconds
     509        for _ in range(3):
     510            select.select([r_pipe], [], [], 3.0)
     511            buf += os.read(r_pipe, 256)
     512            # TODO: add time out to avoid infite blocking.
     513            if buf[-1] == '\n':
     514                break
     515            # gracious 1.0 second
     516            time.sleep(1.0)
     517        if len(buf) == 0:
     518            raise OSError("Not able to alocate a display properly")
     519        if not re.match("^\d+$", buf):
     520            raise OSError("Not able to alocate a display properly")
     521        display_name = ":" + buf[:-1]
     522        sys.stdout.write("Dynamicaly openned display = %s\n" % display_name)
     523    else:
     524        # allocate display automaticaly
     525        xvfb_cmd = xvfb_str.split()+[display_name]
     526        xvfb_executable = xvfb_cmd[0]
     527        xvfb_cmd[0] = "%s-for-Xpra-%s" % (xvfb_executable, display_name)
     528        xvfb = subprocess.Popen(xvfb_cmd, executable=xvfb_executable, close_fds=True,
    480529                                stdin=subprocess.PIPE, preexec_fn=setsid)
     530
    481531    from xpra.os_util import get_hex_uuid
    482532    xauth_cmd = ["xauth", "add", display_name, "MIT-MAGIC-COOKIE-1", get_hex_uuid()]
    483533    try:
    def start_Xvfb(xvfb_str, display_name): 
    487537    except OSError, e:
    488538        #trying to continue anyway!
    489539        sys.stderr.write("Error running \"%s\": %s\n" % (" ".join(xauth_cmd), e))
    490     return xvfb
     540    return xvfb, display_name
    491541
    492542def check_xvfb_process(xvfb=None):
    493543    if xvfb is None:
    def run_server(parser, opts, mode, xpra_file, extra_args): 
    622672        from xpra.scripts.main import guess_X11_display
    623673        display_name = guess_X11_display()
    624674    else:
    625         if len(extra_args) != 1:
    626             parser.error("need exactly 1 extra argument")
    627         display_name = extra_args.pop(0)
    628 
    629     if not shadowing and not proxying:
    630         display_name_check(display_name)
    631 
     675        if len(extra_args) > 1:
     676            parser.error("too much extra arguments")
     677        if len(extra_args) == 1:
     678            display_name = extra_args[0]
     679            display_name_check(display_name)
     680        else:
     681            # We will try to find one automaticaly
     682            display_name = None
     683   
    632684    if not shadowing and not proxying and opts.exit_with_children and not opts.start_child:
    633685        sys.stderr.write("--exit-with-children specified without any children to spawn; exiting immediately")
    634686        return  1
    def run_server(parser, opts, mode, xpra_file, extra_args): 
    644696    # change if/when we daemonize:
    645697    script = xpra_runner_shell_script(xpra_file, os.getcwd(), opts.socket_dir)
    646698
     699    stdout = sys.stdout
     700    stderr = sys.stderr
     701   
    647702    # Daemonize:
    648703    if opts.daemon:
    649704        #daemonize will chdir to "/", so try to use an absolute path:
    650705        if opts.password_file:
    651706            opts.password_file = os.path.abspath(opts.password_file)
    652 
    653         logfd = open_log_file(dotxpra, opts.log_file, display_name)
     707        # At this point we may not know the display name, then create a
     708        # temporary file the we will rename later
     709        #logfd = open_log_file(dotxpra, opts.log_file, display_name)
     710        log_filename0 = select_log_file(dotxpra, opts.log_file, display_name)
     711        logfd = open_log_file(log_filename0)
    654712        assert logfd > 2
    655         daemonize(logfd)
     713        stdout, stderr = daemonize(logfd)
     714        stdout.write("Outputs are now redirected to %s\n" % log_filename0)
    656715
    657716    # Write out a shell-script so that we can start our proxy in a clean
    658717    # environment:
    def run_server(parser, opts, mode, xpra_file, extra_args): 
    661720    from xpra.log import Logger
    662721    log = Logger("server")
    663722
     723    # Do this after writing out the shell script:
     724    if display_name:
     725        os.environ["DISPLAY"] = display_name
     726    sanitize_env()
     727
     728    # Start the Xvfb server first to get the display_name if needed
     729    xvfb = None
     730    xvfb_pid = None
     731    if not shadowing and not proxying and not clobber:
     732        try:
     733            xvfb, display_name = start_Xvfb(opts.xvfb, display_name)
     734        except OSError, e:
     735            log.error("Error starting Xvfb: %s\n", e)
     736            return  1
     737        xvfb_pid = xvfb.pid
     738
     739    if opts.daemon:
     740        log_filename1 = select_log_file(dotxpra, opts.log_file, display_name)
     741        if log_filename0 != log_filename1:
     742            os.rename(log_filename0, log_filename1)
     743            stdout.write("Xpra creatated the following session: '%s'\n" % display_name)
     744            stdout.write("Outputs are now redirected to %s\n" % log_filename1)
     745        stdout.close()
     746        stderr.close()
     747    if display_name:
     748        os.environ["DISPLAY"] = display_name
     749
    664750    try:
    665         # Initialize the sockets before the display,
    666         # That way, errors won't make us kill the Xvfb
    667         # (which may not be ours to kill at that point)
     751        # We need display_name here.
    668752        bind_tcp = parse_bind_tcp(opts.bind_tcp)
    669753
    670754        sockets = []
    def run_server(parser, opts, mode, xpra_file, extra_args): 
    690774        log.error("cannot start server: failed to setup sockets: %s", e)
    691775        return 1
    692776
    693     # Do this after writing out the shell script:
    694     os.environ["DISPLAY"] = display_name
    695     sanitize_env()
    696 
    697     xvfb = None
    698     xvfb_pid = None
    699     if not shadowing and not proxying and not clobber:
    700         try:
    701             xvfb = start_Xvfb(opts.xvfb, display_name)
    702         except OSError, e:
    703             log.error("Error starting Xvfb: %s\n", e)
    704             return  1
    705         xvfb_pid = xvfb.pid
    706 
    707777    if not check_xvfb_process(xvfb):
    708778        #xvfb problem: exit now
    709779        return  1