Repository URL to install this package:
|
Version:
18.5.0-1 ▾
|
#!/usr/bin/perl
# (c) Copyright 2001-2018. CodeWeavers, Inc.
use warnings;
use strict;
my $ldpath_envname="LD_LIBRARY_PATH";
# Portable which(1) implementation
sub cxwhich($$;$)
{
my ($dirs, $app, $noexec)=@_;
if ($app =~ /^\//)
{
return $app if ((-x $app or $noexec) and -f $app);
}
elsif ($app =~ /\//)
{
require Cwd;
my $path=Cwd::cwd() . "/$app";
return $path if ((-x $path or $noexec) and -f $path);
}
else
{
foreach my $dir (split /:/, $dirs)
{
return "$dir/$app" if ($dir ne "" and (-x "$dir/$app" or $noexec) and -f "$dir/$app");
}
}
return undef;
}
# Fast dirname() implementation
sub _cxdirname($)
{
my ($path)=@_;
return undef if (!defined $path);
return "." if ($path !~ s!/+[^/]+/*$!!s);
return "/" if ($path eq "");
return $path;
}
# Locate where CrossOver is installed by looking for the directory
# where the cxmenu script is located, unwinding symlinks on the way
sub locate_cx_root(;$)
{
my ($fallback)=@_;
my $argv0=cxwhich($ENV{PATH},$0);
$argv0=$0 if (!defined $argv0);
if ($argv0 !~ m+^/+)
{
require Cwd;
$argv0=Cwd::cwd() . "/$argv0";
}
my $dir=_cxdirname($argv0);
my $bindir=$dir;
$bindir =~ s%/lib$%/bin%;
while (!-x "$bindir/cxmenu" or !-f "$bindir/cxmenu")
{
last if (!-l $argv0);
$argv0=readlink($argv0);
$argv0="$dir/$argv0" if ($argv0 !~ m+^/+);
$dir=_cxdirname($argv0);
$bindir=$dir;
$bindir =~ s%/lib$%/bin%;
}
$bindir =~ s%/(?:\./)+%/%g;
$bindir =~ s%/\.$%%;
$ENV{CX_ROOT}=_cxdirname($bindir);
if ((!-x "$ENV{CX_ROOT}/bin/cxmenu" or !-f "$ENV{CX_ROOT}/bin/cxmenu") and
$fallback)
{
$ENV{CX_ROOT}=$fallback;
}
if (!-x "$ENV{CX_ROOT}/bin/cxmenu" or !-f "$ENV{CX_ROOT}/bin/cxmenu")
{
my $name0=$0;
$name0 =~ s+^.*/++;
print STDERR "$name0:error: could not find CrossOver in '$ENV{CX_ROOT}'\n";
exit 1;
}
return $ENV{CX_ROOT};
}
BEGIN {
unshift @INC, locate_cx_root() . "/lib/perl";
}
use CXLog;
use CXUtils;
# Process command-line options
my $opt_bottle;
my $opt_scope;
my $arg_app;
my $opt_update_only;
my $ux_app;
my $cx_app;
my $wl_app;
my $wl32_app;
my $cx_hooks;
my $opt_start;
my $opt_start_only;
my $opt_start_default;
my $opt_start_mime;
my $opt_start_class;
my $opt_start_verb;
my $opt_wait_all;
my $opt_check; # For backward compatibility
my $opt_desktop;
my $opt_workdir;
my $opt_convert;
my $opt_quotes;
my $opt_lock;
my $opt_update;
my $opt_display;
my $opt_gui=1;
my $opt_new_console;
my $opt_allow_root;# For backward compatibility
my $opt_wait;
my $opt_wait_children;
my $opt_untrusted;
my $opt_verbose=1 if (CXLog::is_on());
my $opt_cx_log;
my $opt_debugmsg=$ENV{CX_DEBUGMSG};
my $opt_dlloverrides;
my $opt_dashdash;
my $opt_winver;
my $opt_version;
my $opt_help;
my $opt_demo_status;
my $opt_demo_nag;
my $opt_enable_alt_loader;
require CXOpts;
my $cxopts=CXOpts->new(["stop_on_unknown","stop_on_non_option"]);
$cxopts->add_options(["bottle=s" => \$opt_bottle,
"scope=s" => \$opt_scope,
"update-only" => \$opt_update_only,
"ux-app=s" => \$ux_app,
"cx-app=s" => \$cx_app,
"wl-app=s" => \$wl_app,
"wl32-app=s" => \$wl32_app,
"cx-hooks=s" => \$cx_hooks,
"start=s" => \$opt_start,
"start-only=s" => \$opt_start_only,
"start-default=s" => \$opt_start_default,
"start-mime=s" => \$opt_start_mime,
"start-class=s" => \$opt_start_class,
"start-verb=s" => \$opt_start_verb,
"desktop=s" => \$opt_desktop,
"workdir=s" => \$opt_workdir,
"convert!" => \$opt_convert,
"quotes!" => \$opt_quotes,
"lock!" => \$opt_lock,
"update!" => \$opt_update,
"check" => \$opt_check,
"allow-root!" => \$opt_allow_root,
"untrusted!" => \$opt_untrusted,
"debugmsg=s" => \$opt_debugmsg,
"dll=s" => \$opt_dlloverrides,
"winver=s" => \$opt_winver,
"wait!" => \$opt_wait,
"wait-children" => \$opt_wait_children,
"wait-all" => \$opt_wait_all,
"display=s" => \$opt_display,
"gui!" => \$opt_gui,
"new-console" => \$opt_new_console,
"verbose!" => \$opt_verbose,
"cx-log=s" => \$opt_cx_log,
"" => \$opt_dashdash,
"version" => \$opt_version,
"demo-status" => \$opt_demo_status,
"demo-nag" => \$opt_demo_nag,
"enable-alt-loader=s" => \$opt_enable_alt_loader,
"?|h|help" => \$opt_help
]);
my $err=$cxopts->parse();
if ($opt_verbose)
{
CXLog::fdopen(2);
$opt_cx_log="-" if (!defined $opt_cx_log);
}
my $default_warning;
if (!defined $opt_bottle and !defined $ENV{CX_BOTTLE})
{
$default_warning="";
$opt_bottle="default";
}
$ENV{CX_BOTTLE}=$opt_bottle if (defined $opt_bottle);
# Validate the command line options
my $usage;
if ($err)
{
cxerr("$err\n");
$usage=2;
}
elsif ($opt_help)
{
$usage=0;
}
elsif ($opt_version)
{
$usage=3;
}
else
{
if (cxname0() eq "cxstart")
{
if (defined $opt_start)
{
cxerr("--start cannot be used with cxstart\n");
$usage=2;
}
elsif (!@ARGV)
{
cxerr("you must specify the document to open with cxstart\n");
$usage=2;
}
else
{
$opt_start=shift @ARGV;
}
}
if (!defined $opt_cx_log)
{
$opt_cx_log = "-";
$opt_debugmsg="-all" if (!defined $opt_debugmsg);
}
elsif ($opt_cx_log eq "/dev/null" and !defined $ux_app)
{
# No point sending traces to /dev/null
$opt_debugmsg = "-all";
}
$ENV{DISPLAY}=$opt_display if (defined $opt_display);
$ENV{CX_DEBUGMSG}=$opt_debugmsg if (defined $opt_debugmsg);
my $cmd_count=0;
$cmd_count++ if (defined $opt_update_only);
$cmd_count++ if (defined $ux_app);
$cmd_count++ if (defined $cx_app);
$cmd_count++ if (defined $wl_app);
$cmd_count++ if (defined $wl32_app);
$cmd_count++ if (defined $cx_hooks);
$cmd_count++ if (defined $opt_start);
$cmd_count++ if (defined $opt_wait_all);
$cmd_count++ if (defined $opt_demo_status);
$cmd_count++ if (defined $opt_demo_nag);
if ((@ARGV or $cmd_count > 1) and ($opt_demo_status or $opt_demo_nag))
{
cxerr("--demo-status and --demo-nag must be used alone\n");
$usage=2;
}
elsif ($cmd_count>1)
{
cxerr("--cx-app, --ux-app, --wl-app, --wl32-app, --cx-hooks, --start, --wait-all and --update-only are mutually exclusive\n");
$usage=2;
}
elsif ($cmd_count == 0)
{
if (cxname0() ne "wine")
{
$arg_app=cxname0();
}
else
{
if (!@ARGV)
{
cxerr("you must specify an application to run\n");
$usage=2;
}
elsif (!$opt_dashdash and @ARGV > 1 and $ARGV[1] eq "--")
{
splice @ARGV, 1, 1;
}
}
}
if (!defined $opt_wait)
{
$opt_wait=1;
}
elsif (defined $opt_wait_children)
{
cxerr("--wait and --wait-children are mutually incompatible\n");
$usage=2;
}
if (defined $opt_start)
{
my $start_count=0;
$start_count++ if (defined $opt_start_default);
$start_count++ if (defined $opt_start_mime);
$start_count++ if (defined $opt_start_class);
if ($start_count > 1)
{
cxerr("--start-default, --start-mime and --start-class are mutually incompatible\n");
$usage=2;
}
}
elsif (defined $opt_start_only or defined $opt_start_default or
defined $opt_start_mime or defined $opt_start_class or
defined $opt_start_verb)
{
cxerr("--start-only, --start-default, --start-mime, --start-class and --start-verb can only be used with --start\n");
$usage=2;
}
if (defined $opt_scope)
{
$opt_scope=~ tr/A-Z/a-z/ ;
if ($opt_scope !~ /^(?:managed|private)$/)
{
cxerr("unknown scope value '$opt_scope'\n");
$usage=2;
}
}
if (defined $ux_app or defined $cx_hooks)
{
if (defined $opt_convert or defined $opt_quotes)
{
cxerr("--convert and --quotes are incompatible with --ux-app and --cx-hooks\n");
$usage=2;
}
}
else
{
$opt_convert=1 if (!defined $opt_convert);
$opt_quotes=1 if (!defined $opt_quotes);
}
$opt_lock=1 if (!defined $opt_lock);
$opt_update=1 if (!defined $opt_update and $opt_update_only);
$opt_update=1 if (!defined $opt_update and $opt_lock);
if (!$opt_update and $opt_update_only)
{
cxerr("--no-update cannot be used with --update-only\n");
$usage=2;
}
if ($opt_update and !$opt_lock)
{
cxerr("--update cannot be used with --no-lock\n");
$usage=2;
}
if ($opt_update_only and !$opt_lock)
{
cxerr("--update-only cannot be used with --no-lock\n");
$usage=2;
}
}
sub get_template_directory($)
{
require CXBottle;
my $dir=CXBottle::get_template_directory($_[0]);
if (!defined $dir)
{
cxerr("unsupported template type for bottle '$ENV{CX_BOTTLE}'\n");
exit 1;
}
return $dir;
}
sub fatal_error(@)
{
if ($opt_gui)
{
cxmessage("-title", "Fatal Error",
"-buttons", "OK",
"-default", "OK",
"-image", "crossover",
@_);
}
else
{
cxerr(sprintf($_[0], splice(@_, 1)));
}
exit 1;
}
# Scan for viruses and/or warn about running untrusted files (attachments)
sub check_file($$$$$)
{
my ($cxconfig, $untrusted, $mode, $win_file, $filename)=@_;
if (!defined $cxconfig)
{
require CXBottle;
$cxconfig=CXBottle::get_crossover_config();
$cxconfig->read("$ENV{WINEPREFIX}/cxbottle.conf");
}
my $avscan=$cxconfig->get("CrossOver", "AntiVirusScan", "start");
if (($avscan eq "start" and ($untrusted or $win_file)) or
($avscan eq "untrusted" and $untrusted))
{
my $cmd=shquote_string("$ENV{CX_ROOT}/bin/cxavscan") . " " .
($opt_gui ? "--gui " : "") . shquote_string($filename);
my $output=cxbackquote($cmd);
return if ($? == 0); # No virus found
exit 1 if ($? == 256); # Found a virus -> abort
exit 2 if ($? == 512); # The antivirus does not work
# No antivirus scanner was available, continue the regular checks
}
return if (!$untrusted);
my $tmp=$ENV{TMPDIR} || "/tmp";
$tmp =~ s!/+!/!g;
$tmp =~ s!/$!!;
$filename =~ s!/+!/!g;
return if ($filename !~ m%^\Q$tmp\E/%);
my $allow_untrusted=$cxconfig->get("Bottle", "AllowUntrusted");
return if ($allow_untrusted);
exit 1 if (!$opt_gui);
my $rc=cxmessage(
"-title", "CrossOver Security Warning",
"-buttons", "Continue:0,Always Continue:2,Abort:3",
"-default", "Abort",
"-image", "crossover",
"-named-args",
"You are about to run a Windows application or open a " .
"document which appears to come from an untrusted " .
"source (most likely an email attachment):\n" .
"\n" .
"\%(filename)s\n" .
"\n" .
"If this is a virus, running it could put your " .
"computer at risk.\n" .
"Click on 'Abort' to stop the launch process before any " .
"harm has been done. Click on 'Continue' to open the above " .
"application or document, or click on 'Always Continue' to " .
"continue and not get asked this question again.", "filename", $filename);
if ($rc == 512)
{
my $lock=CXBottle::lock_bottle($ENV{WINEPREFIX});
require CXRWConfig;
my $filename="$ENV{WINEPREFIX}/cxbottle.conf";
my $cxrwconfig=CXRWConfig->new($filename);
$cxrwconfig->set("Bottle", "AllowUntrusted", "1");
if ($mode eq "stub" and -l $filename)
{
# Break the stub's symbolic link so we can save the change
unlink $filename;
}
if (!$cxrwconfig->save())
{
cxwarn("unable to save '$ENV{WINEPREFIX}/cxbottle.conf': $!\n");
}
CXUtils::cxunlock($lock);
}
elsif ($rc != 0)
{
exit 2;
}
}
sub get_updater($)
{
my ($cxconfig)=@_;
my $updater=expand_string($cxconfig->get("Bottle", "Updater"));
if (!$updater)
{
$updater=$cxconfig->get("Default", "WinePrefixCreate");
if (defined $updater)
{
$updater =~ s/wineprefixcreate\.sh$/wineprefixcreate/;
$updater =~ s/^.*wineprefixfronskel$//;
}
}
return $updater;
}
sub run_python_hook($)
{
my ($cxconfig)=@_;
my $next_hook=$cxconfig->get("CrossOver", "NextUpdateHookDate", "0");
$next_hook =~ s/^0*(?=.)//; # treat as a decimal integer
my $run_hook=eval "time() > $next_hook"; # catch integer overflows, etc.
$run_hook=1 if ($@);
my $hook="$ENV{CX_ROOT}/bin/cxupdatecheck";
if ($run_hook and -x $hook)
{
# Use a double-fork to avoid zombies
my $pid=fork();
if (defined $pid and $pid == 0)
{
$pid=fork();
cxexec(shquote_string($hook)) if (defined $pid and $pid == 0);
exit 0;
}
waitpid($pid, 0) if (defined $pid);
}
}
sub prepend_env_path($$)
{
my ($name, $prefix)=@_;
my $path=$ENV{$name};
if (!defined $path or $path eq "")
{
$ENV{$name}=$prefix;
}
elsif ($path !~ /^\Q$prefix\E(:|$)/)
{
$ENV{$name}="$prefix:$path";
}
}
sub dump_wine_environment($)
{
return if (!CXLog::is_on());
my ($inherited)=@_;
cxlog($inherited ? "Inherited " : "", "Environment:\n");
foreach my $name ("CX_ROOT", "CX_BOTTLE", "WINEPREFIX",
"CX_WINDOWS_VERSION", "PATH", $ldpath_envname,
"WINEDLLPATH", "WINEDLLOVERRIDES",
"LD_PRELOAD", "LD_ASSUME_KERNEL",
"WINELOADER", "WINESERVER", "WINEDEBUG",
"CX_LOG", "CX_DEBUGMSG", "CX_WINE_USAGE_LOGFILE",
"DISPLAY")
{
cxlog(" $name = ",(defined $ENV{$name}?"\"$ENV{$name}\"":"<undefined>"),"\n");
}
}
# Setup the environment
my ($cxconfig, $mode);
require CXBottle;
if (defined $usage)
{
# Don't bother checking the bottle specification
}
elsif (!CXBottle::is_initialized())
{
$cxconfig=CXBottle::get_crossover_config();
cxlog("Product version=", $cxconfig->get("CrossOver", "ProductVersion", "<unset>"), "\n");
# Locate the bottle
my ($scope, $raw_wineprefix)=CXBottle::setup_bottle_wineprefix($opt_scope);
if (!defined $scope)
{
fatal_error("\%s", $@);
exit 1;
}
$default_warning=$@ if (defined $default_warning);
# The environment setup (and anything accessing the content of the bottle)
# must be serialized
my $lock;
$lock=CXBottle::lock_bottle($ENV{WINEPREFIX}) if ($opt_lock);
my ($updater_cmd, $pre_notify_hooks, $notify_hooks);
if ($opt_update)
{
# We cannot use '-e _' here
if (-l "$ENV{WINEPREFIX}/cxbottle.conf" and !-e "$ENV{WINEPREFIX}/cxbottle.conf")
{
# The managed bottle has probably been moved, try to recover
my $managedprefix=CXBottle::find_bottle($ENV{CX_BOTTLE}, "managed");
if (!defined $managedprefix)
{
fatal_error("'cxbottle.conf' is not readable. Check the permissions of the managed bottle files in '\%s'\n", $raw_wineprefix);
}
$cxconfig->read("$managedprefix/cxbottle.conf");
my $updater=get_updater($cxconfig);
if (!defined $updater)
{
fatal_error("invalid managed bottle: 'Updater' is not set in '\%s/cxbottle.conf'\n", $managedprefix);
}
cxlog("Mode = 'broken stub'\n");
$mode="stub";
$updater_cmd=[$updater, "--force", "--ref-dir", $managedprefix, $ENV{WINEPREFIX}];
}
elsif ($scope eq "managed" and ($opt_scope || "") ne "managed")
{
if (!-r "$ENV{WINEPREFIX}/cxbottle.conf")
{
fatal_error("'cxbottle.conf' is not readable. Check the permissions of the managed bottle files in '\%s'\n", $raw_wineprefix);
}
$cxconfig->read("$ENV{WINEPREFIX}/cxbottle.conf");
my $updater=get_updater($cxconfig);
if (!defined $updater)
{
fatal_error("invalid managed bottle: 'Updater' is not set in '\%s/cxbottle.conf'\n", $raw_wineprefix);
}
# Create the stub bottle
# Note that we can also get there if the bottle was found via the
# managed default bottle link.
cxlog("Mode = 'stub' or 'new-stub'\n");
$mode="stub";
# Use $raw_wineprefix as we the stub's symbolic links *must* go
# through the official path.
my $managedprefix=$raw_wineprefix;
$raw_wineprefix=CXBottle::compute_new_wineprefix($ENV{CX_BOTTLE}, "private");
if (!defined $raw_wineprefix)
{
fatal_error("unable to create a stub for the '\%s' bottle: \%s\n", $ENV{CX_BOTTLE}, $@);
}
$ENV{WINEPREFIX}=CXUtils::cxrealpath($raw_wineprefix);
$updater_cmd=[$updater, "--ref-dir", $managedprefix, $ENV{WINEPREFIX}];
}
else
{
if (!-r "$ENV{WINEPREFIX}/cxbottle.conf")
{
fatal_error("'cxbottle.conf' is not readable. Check the permissions of files in '$raw_wineprefix'\n");
}
$cxconfig->read("$ENV{WINEPREFIX}/cxbottle.conf");
my $updater=get_updater($cxconfig);
if ($updater and $scope ne "managed")
{
# Check if the bottle needs an update
cxlog("Mode = 'stub'\n");
$mode="stub";
my $managedprefix=CXBottle::find_bottle($ENV{CX_BOTTLE}, "managed");
if (!defined $managedprefix)
{
fatal_error("unable to find the managed '\%s' bottle: \%s\n", $ENV{CX_BOTTLE}, $@);
}
# Don't dereference $managedprefix as the stub's symbolic
# links *must* go through the official path.
$updater_cmd=[$updater, "--ref-dir", $managedprefix, $ENV{WINEPREFIX}];
}
else
{
# Check if the bottle needs an upgrade
cxlog("Mode = '$scope'\n");
$mode=$scope;
umask(umask() & ~0055) if ($scope eq "managed" and $> == 0);
my $crossover_t=$cxconfig->get("CrossOver", "BuildTimestamp", "");
my $bottle_t=$cxconfig->get("Bottle", "Timestamp", "");
if ($crossover_t ne $bottle_t)
{
# only one element so this will be interpreted as a
# command line
my $template_dir=get_template_directory($cxconfig);
$updater_cmd=[shquote_string("$template_dir/setup")];
}
}
}
}
# Set the environment first so that the configuration below overrides it
CXBottle::set_environment($cxconfig, "Bottle environment variables");
my $bin_path = expand_string($cxconfig->get("Wine", "BinPath", "$ENV{CX_ROOT}/bin"));
my $lib_path = $cxconfig->get("Wine", "LibPath");
if (!defined $lib_path)
{
$lib_path = "$ENV{CX_ROOT}/lib";
$lib_path .= ":$ENV{CX_ROOT}/lib/samba" if (-d "$ENV{CX_ROOT}/lib/samba");
}
$lib_path = expand_string($lib_path);
my $dll_path = expand_string($cxconfig->get("Wine", "DllPath") ||
join( ":", map { $_ .= "/wine"; } grep { -d "$_/wine" } split( /:/, $lib_path )));
my $lib64_path;
if ($cxconfig->get("Bottle", "WineArch", "win32") eq "win64" and -d "$ENV{CX_ROOT}/lib64")
{
$lib64_path = "$ENV{CX_ROOT}/lib64";
}
my $ld_preload = expand_string($cxconfig->get("Wine", "LDPreload"));
my $ld_assume_kernel = expand_string($cxconfig->get("Wine", "LDAssumeKernel"));
run_python_hook($cxconfig) if ($scope ne "managed");
my $user_dir = CXBottle::get_user_dir();
if ($scope ne "managed" and defined $user_dir)
{
if ($cxconfig->get("CrossOver", "ReportWineUsage", 1))
{
# Set a log file for winewrapper even if logging is unset.
# This way if the user decides to turn it on later, we don't lose
# accumulated info.
$ENV{CX_WINE_USAGE_LOGFILE} = "$user_dir/usage.log";
# If the user has actually turned reporting on, then upload
# the logged info occasionally.
if ($cxconfig->get("CrossOver", "ReportWineUsage", 0))
{
my $next_report = $cxconfig->get("CrossOver", "NextUsageReportDate", "0");
$next_report =~ s/^0*(?=.)//; # treat as a decimal integer
my $report=eval "time() > $next_report"; # catch overflows, etc.
$report = 1 if ($@);
if ($report and fork() == 0)
{
# Do the reporting in the background
cxexec(shquote_string("$ENV{CX_ROOT}/bin/cxreportusage"));
exit 1;
}
}
}
else
{
# If the user has told us not to log, then delete the log file.
# That's just good manners.
unlink("$user_dir/usage.log");
}
}
$ENV{CX_INITIALIZED}="$>:$ENV{CX_BOTTLE}";
# $ENV{WINEPREFIX} is set already
delete $ENV{CX_WINDOWS_VERSION};
$ENV{CX_WINDOWS_VERSION}=$opt_winver if (defined $opt_winver);
prepend_env_path("PATH", $bin_path);
prepend_env_path("LD_PRELOAD", $ld_preload) if (defined $ld_preload);
$ENV{LD_ASSUME_KERNEL}=$ld_assume_kernel if (defined $ld_assume_kernel);
$ENV{WINELOADER}=cxwhich($bin_path,"wineloader") // cxwhich($bin_path,"wineloader64");
$ENV{WINESERVER}=cxwhich($bin_path,"wineserver");
$ENV{WINEDEBUG}=$opt_debugmsg if (defined $opt_debugmsg);
$ENV{DYLD_FALLBACK_LIBRARY_PATH}=$ENV{CX_DYLD_FALLBACK_LIBRARY_PATH} if (defined $ENV{CX_DYLD_FALLBACK_LIBRARY_PATH} and !defined $ENV{DYLD_FALLBACK_LIBRARY_PATH});
prepend_env_path($ldpath_envname, $lib_path);
prepend_env_path($ldpath_envname, $lib64_path) if (defined $lib64_path);
$ENV{WINEDLLPATH}=$dll_path;
delete $ENV{WINEDLLOVERRIDES};
$ENV{WINEDLLOVERRIDES}=$opt_dlloverrides if (defined $opt_dlloverrides);
dump_wine_environment(0);
# And update / upgrade the bottle if necessary
if (defined $updater_cmd)
{
my $pid;
if ($opt_gui)
{
# Wait just a second before popping up the 'please wait' dialog
# in the hope that the update / upgrade will be over by then.
# This avoids unnecessary visual intrusions.
$pid=cxwait(1, "--no-focus",
"Preparing the \%s bottle", $ENV{CX_BOTTLE});
}
cxlog("Updating/upgrading the WinePrefix directory:\n");
my $rc=cxsystem(@$updater_cmd);
if (defined $pid)
{
# Kill and wait for it to avoid creating a zombie
kill(15, $pid);
waitpid($pid, 0);
}
if ($rc != 0 and $rc != 768)
{
if (!-d $ENV{WINEPREFIX})
{
fatal_error("Unable to start the application or open the ".
"document because the creation of the Wine ".
"environment (\%s) failed", $ENV{WINEPREFIX});
}
elsif ($opt_gui)
{
cxmessage(
"-title","CrossOver Warning",
"-buttons","OK:0",
"-default","OK",
"-image","crossover",
"An error occurred during the Wine environment (\%s) ".
"creation or update. The application or document may ".
"fail to start or open.", $ENV{WINEPREFIX});
}
exit(1) if ($opt_update_only);
# Else continue anyway since that's what we do normally
}
}
# FIXME: Do we need to do an is_wineprefix_valid() check around here???
# End serialized section
CXUtils::cxunlock($lock);
}
else
{
dump_wine_environment(1);
}
# Print usage (requires that the environment be setup)
if (defined $usage)
{
if ($usage==3)
{
$cxconfig=CXBottle::get_crossover_config();
print "Product Name: ", CXUtils::get_product_name(), "\n";
print "Public Version: 18.5.0\n";
foreach my $name ("Product Version", "Build Tag", "Build Timestamp")
{
print "$name: ";
my $prop=$name;
$prop =~ s/ //g;
print $cxconfig->get("CrossOver", $prop, "<unknown>"), "\n";
}
exit 0;
}
my $name0=cxname0();
if ($usage)
{
cxerr("try '$name0 --help' for more information\n");
exit $usage;
}
print "Usage: $name0 [--bottle BOTTLE] [command]\n";
print " [options] [application arguments]\n";
print "\n";
if ($name0 ne "cxstart")
{
print "Runs Windows, Winelib or Native applications in the Wine environment. With the\n";
print "--start command, documents can also be opened using the Windows associations.\n";
print "\n";
print "Where command is one of:\n";
print " --cx-app FILE Run the specified Windows executable\n";
print " --wl-app FILE Run the specified Winelib executable\n";
print " --wl32-app FILE Run the specified 32-bit Winelib executable\n";
print " --ux-app FILE Run the specified Native executable\n";
print " Native paths are not converted to Windows paths\n";
print " --cx-hooks ACTION Runs the specified action through the CrossOVer hooks.\n";
print " --start FILE Open or run the specified document\n";
print " --update-only Only update the bottle\n";
print " --help, -h Shows this help message\n";
print " --version Outputs the version information and exits\n";
print "If no command is specified, the first non option argument is used as the name\n";
print "of the Windows application to run.\n";
}
else
{
print "Opens the specified document using the Windows associations.\n";
print "\n";
print "Where command is one of:\n";
print " --help, -h Shows this help message\n";
print " --version Outputs the version information and exits\n";
}
print "\n";
print "Options:\n";
print " --bottle BOTTLE Use the specified bottle. If this option is not used,\n";
print " fallback to \$CX_BOTTLE and then to 'default'\n";
print " --scope SCOPE If set to managed, the bottle will be looked up in the\n";
print " system-wide bottle locations, otherwise it will refer to a\n";
print " private bottle\n";
print " --start-only EXTLIST Only allow document filenames ending with one of the\n";
print " extensions in this colon-separated list\n";
print " --start-default MIME Assume the document is of the specified MIME type\n";
print " if it has no extension\n";
print " --start-mime MIME Use the specified MIME type to open the document\n";
print " instead of selecting it based on the file extension\n";
print " --start-class NAME Use the specified Windows association to open the document\n";
print " instead of selecting it based on the file extension\n";
print " --start-verb VERB Perform the specified Windows action on the document\n";
print " --workdir DIRECTORY Change the current directory as specified before\n";
print " starting the application, but after converting native paths\n";
print " to Windows paths\n";
print "\n";
print "Advanced options:\n";
print " --desktop NAME The Wine desktop to run the command in\n";
print " --dll OVERRIDES Specifies dll overrides\n";
print " See the Wine documentation for more details\n";
print " --winver VERSION Overrides the default Windows version\n";
print " See the Wine documentation for more details\n";
print " --no-convert Don't convert native paths to Windows paths\n";
print " --no-quotes Don't quote arguments, even if they contain spaces or\n";
print " special characters\n";
print " --no-update Don't update or upgrade the bottle\n";
print " --no-lock Don't lock, update or upgrade the bottle. Must be used with\n";
print " extreme care\n";
print " --untrusted The executable or document may come from an untrusted source\n";
print " --no-wait Exit immediately after the process is started\n";
print " --wait Wait for the process to exit. This is the default\n";
print " --wait-children Wait for the process and all its children to exit\n";
print " --wait-all Wait for all Wine processes to exit\n";
print " --display DISPLAY Sets the X display as specified\n";
print " --no-gui Don't create a dialog if an error occurs\n";
print " --new-console Create a window for console applications\n";
print " --verbose Output more information about what is going on\n";
print " --cx-log LOGFILE Sends debugging output to the specified file\n";
print " This overrides \$CX_LOG. If the file name ends with '.z',\n";
print " '.gz' or '.bz2', the file will be compressed on the fly\n";
print " with compress, gzip or bzip2 respectively\n";
print " --debugmsg CHANNELS Specifies a list of debugging channels to enable or\n";
print " disable. This overrides \$CX_DEBUGMSG\n";
print " See the Wine documentation for more details\n";
print "\n";
print "Any remaining argument is handed off to the Windows application.\n";
print "Arguments matching an existing native file are converted to a Windows path\n";
print "unless --no-convert is used.\n";
exit 0;
}
if ($opt_update_only)
{
exit(0);
}
if ($opt_wait_all)
{
# If exec fails we will get an error message and a return code of 2
exec $ENV{WINESERVER}, "-w";
}
# Search for the application
sub is_valid_win_path($)
{
my ($path) = @_;
return 1 if ($path !~ /[*?<>":]/);
return 0 if ($path !~ s/^[a-zA-Z]://);
return 1 if ($path !~ /[*?<>":]/);
return 0;
}
my $cmd;
my @wine_args;
if (defined $ux_app)
{
$cmd=$ux_app;
}
elsif (defined $cx_hooks)
{
CXBottle::run_bottle_hooks([$cx_hooks, @ARGV]);
exit 0;
}
elsif (defined $opt_start)
{
if (!is_valid_win_path($opt_start))
{
my $path;
foreach my $entry (split m!/!, $opt_start)
{
$path=(defined $path ? "$path/" : "");
$path.=$entry;
if ($entry =~ /[*?<>":]/ and -l $path)
{
my $target=readlink($path);
if ($target =~ m+^/+)
{
$path=$target;
}
else
{
$path=cxdirname($path) . "/$target";
}
}
}
$opt_start=$path;
if (!is_valid_win_path($opt_start) and -e $opt_start)
{
cxwarn("'$opt_start' contains characters that are not allowed in Windows filenames (*?<>\":) so the Windows application may not succeed in opening it\n");
}
}
if (!defined $opt_start_mime and !defined $opt_start_class)
{
my $bad_extension;
my $ext=$opt_start;
if ($ext =~ s/^.*\././)
{
if ($opt_start_only)
{
$bad_extension=1;
foreach my $allowed (split /:+/, $opt_start_only)
{
if ($ext =~ /^\Q$allowed\E$/i)
{
$bad_extension=0;
last;
}
}
$opt_start_mime=$opt_start_default if ($bad_extension);
}
}
else
{
$opt_start_mime=$opt_start_default;
$bad_extension=1 if ($opt_start_only and !defined $opt_start_default);
}
if ($bad_extension)
{
exit 1 if (!$opt_gui);
my $rc;
if ($opt_start_default)
{
$rc=cxmessage(
"-title", "CrossOver Warning",
"-buttons", "Continue:0,Abort:2",
"-default", "Abort",
"-image", "crossover",
"-named-args", "WARNING: The extension of the " .
"'\%(filename)s' document does not match the expected " .
"MIME type: \%(mime)s. This could indicate malicious " .
"activity.\n\n" .
"Click on 'Continue' to ignore the document extension " .
"and open it as a \%(mime)s file, or click on 'Abort' to stop " .
"now.",
"filename", $opt_start, "mime", $opt_start_default);
}
else
{
$rc=cxmessage(
"-title", "CrossOver Warning",
"-buttons", "Abort:2",
"-default", "Abort",
"-image", "crossover",
"-named-args", "Not opening the '\%(filename)s' document " .
"because its extension does not match any of the allowed " .
"extensions: \%(extensions)s. This could indicate " .
"malicious activity.\n\n" .
"If you wish to open it anyway, save it to disk, " .
"verify its extension, and then open it manually.",
"filename", $opt_start, "extensions", $opt_start_only);
}
exit 1 if ($rc);
}
}
$cmd=$ENV{WINELOADER};
unshift @ARGV, $opt_start;
unshift @wine_args, "--start", "--";
unshift @wine_args, "--desktop", $opt_desktop if ($opt_desktop);
unshift @wine_args, "--workdir", $opt_workdir if ($opt_workdir);
unshift @wine_args, "--no-convert" if (!$opt_convert);
unshift @wine_args, "--no-quotes" if (!$opt_quotes);
unshift @wine_args, "--start-mime", $opt_start_mime if ($opt_start_mime);
unshift @wine_args, "--start-class", $opt_start_class if ($opt_start_class);
unshift @wine_args, "--start-verb", $opt_start_verb if ($opt_start_verb);
unshift @wine_args, "--no-wait" if (!$opt_wait);
unshift @wine_args, "--wait-children" if ($opt_wait_children);
unshift @wine_args, "--new-console" if ($opt_new_console);
unshift @wine_args, "--enable-alt-loader", $opt_enable_alt_loader if ($opt_enable_alt_loader);
unshift @wine_args, "winewrapper.exe";
}
else
{
my $wl_searchpath;
$cmd=$ENV{WINELOADER};
if (defined $arg_app)
{
# Use the name we were invoked as, to find either a WineLib
# application, or a Windows executable
$arg_app .= ".exe" unless ($arg_app =~ /\.exe$/i);
$wl_app = cxwhich($ENV{WINEDLLPATH}, "$arg_app.so", 1);
if (!defined $wl_app)
{
# We could not find a Winelib application so we assume it
# is a Windows executable.
$cx_app=$arg_app;
}
}
if (defined $wl_app)
{
my $cxconfig;
require CXBottle;
$cxconfig=CXBottle::get_crossover_config();
$cxconfig->read("$ENV{WINEPREFIX}/cxbottle.conf");
if ($cxconfig->get("Bottle", "WineArch", "win32") eq "win64")
{
# FIXME: Make it possible to override this?
$wl_searchpath="$ENV{CX_ROOT}/lib64/wine:$ENV{WINEDLLPATH}";
}
else
{
$wl_searchpath=$ENV{WINEDLLPATH};
}
}
elsif (defined $wl32_app)
{
$wl_app=$wl32_app;
$wl_searchpath=$ENV{WINEDLLPATH};
}
if (defined $wl_app)
{
my $exe_name = $wl_app;
if ($exe_name =~ /\.exe$/)
{
$exe_name .= ".so";
}
elsif ($exe_name !~ /\.exe\.so$/)
{
$exe_name .= ".exe.so";
}
$exe_name = cxwhich($wl_searchpath, $exe_name, 1);
if (!defined $exe_name)
{
cxerr("could not find '$wl_app' in '$wl_searchpath'\n");
exit 2;
}
unshift @ARGV, $exe_name;
unshift @wine_args, "--run","--" if (!$opt_demo_status and !$opt_demo_nag);
unshift @wine_args, "--desktop", $opt_desktop if ($opt_desktop);
unshift @wine_args, "--no-wait" if (!$opt_wait);
unshift @wine_args, "--wait-children" if ($opt_wait_children);
unshift @wine_args, "--new-console" if ($opt_new_console);
unshift @wine_args, "--no-convert" if (!$opt_convert);
unshift @wine_args, "--no-quotes" if (!$opt_quotes);
unshift @wine_args, "--enable-alt-loader", $opt_enable_alt_loader if ($opt_enable_alt_loader);
unshift @wine_args, "winewrapper.exe";
}
else
{
if (defined $cx_app)
{
my $c_root="$ENV{WINEPREFIX}/drive_c";
my @apps;
if ($cx_app =~ /^[a-zA-Z]:/ )
{
push @apps, $cx_app;
}
else
{
my $pattern=CXUtils::glob2regexp($cx_app, 1);
$pattern.="\\.(?i:com|exe)" if ($cx_app !~ /\.(?i:com|exe)$/);
@apps=CXUtils::cxfind($c_root, "^$pattern\$", 2);
}
if (!@apps)
{
cxerr("could not find '$cx_app' in '$c_root'. Is it installed?\n");
exit 2;
}
elsif (@apps > 1)
{
cxerr("multiple matches found for '$cx_app' in '$c_root'\n");
exit 2;
}
unshift @ARGV, @apps;
}
unshift @wine_args, "--demo-nag","--" if ($opt_demo_nag);
unshift @wine_args, "--run","--" if (!$opt_demo_status and !$opt_demo_nag);
unshift @wine_args, "--workdir", $opt_workdir if ($opt_workdir);
unshift @wine_args, "--desktop", $opt_desktop if ($opt_desktop);
unshift @wine_args, "--no-convert" if (!$opt_convert);
unshift @wine_args, "--no-quotes" if (!$opt_quotes);
unshift @wine_args, "--no-wait" if (!$opt_wait);
unshift @wine_args, "--wait-children" if ($opt_wait_children);
unshift @wine_args, "--new-console" if ($opt_new_console);
unshift @wine_args, "--enable-alt-loader", $opt_enable_alt_loader if ($opt_enable_alt_loader);
unshift @wine_args, "winewrapper.exe";
}
}
if (@ARGV and -f $ARGV[0])
{
check_file($cxconfig, $opt_untrusted, $mode, (!defined $ux_app and !defined $wl_app), $ARGV[0]);
}
# Setup the logging
my $log;
if ($opt_cx_log ne "-")
{
my $zipper;
if ($opt_cx_log =~ /\.gz$/)
{
$zipper = CXUtils::get_gzip() . " -2";
}
elsif ($opt_cx_log =~ /\.bz2$/)
{
$zipper = CXUtils::get_bzip2() . " -9";
}
elsif ($opt_cx_log =~ /\.z$/)
{
$zipper = "compress";
}
else
{
$zipper = undef;
}
if ($zipper)
{
$log=undef if (!open($log, "| exec $zipper -c >" . shquote_string($opt_cx_log)));
}
else
{
if (open($log, ">>", $opt_cx_log))
{
truncate($log, 0);
}
else
{
$log=undef;
}
}
cxerr("unable to open the log file: $!\n") if (!$log);
}
my @args;
if (defined $ux_app or !$opt_convert)
{
# Pass the arguments as is
@args=@ARGV;
}
else
{
# Cleanup the arguments
foreach my $arg (@ARGV)
{
# ':switch:' is for backward compatibility
$arg =~ s+:switch:+/+g;
# "~WS~" corresponds to '/' and "~WB~" to '\'
$arg =~ s+~WS~+/+g;
$arg =~ s+~WB~+\\+g;
push @args,$arg;
}
}
# Final checks and logging setup
cxlog("Command:\n");
cxlog("$cmd @wine_args @args\n");
if ($log)
{
# Redirect stderr
open STDERR, ">&=" . fileno($log);
my $tmp=select STDERR; $| = 1; select $tmp; # Make unbuffered
close($log);
}
print STDERR $default_warning if ($default_warning);
# Start Wine
if ($log or CXLog::is_on())
{
print STDERR "\n** ",scalar(localtime(time)),"\n";
print STDERR "Starting '",join("' '",$cmd,@wine_args),"'\n";
print STDERR "'",join("' '",@args),"'\n\n";
}
exec $cmd, @wine_args, @args
or cxerr("unable to start '$cmd': $!\n");
exit 1;