mirror of
https://github.com/Sonarr/Sonarr
synced 2024-12-21 23:33:00 +00:00
New: Replaced launcher on OSX Catalina so that individual permissions can be assigned (note, will ignore permissions previously assigned to sh)
This commit is contained in:
parent
f846e0c031
commit
396caa52cf
30 changed files with 1510 additions and 29 deletions
2
.gitattributes
vendored
2
.gitattributes
vendored
|
@ -5,7 +5,7 @@
|
||||||
# when checked out on windows
|
# when checked out on windows
|
||||||
*.sh text eol=lf
|
*.sh text eol=lf
|
||||||
distribution/debian/* text eol=lf
|
distribution/debian/* text eol=lf
|
||||||
macOS/Sonarr text eol=lf
|
distribution/osx/Sonarr text eol=lf
|
||||||
|
|
||||||
# Custom for Visual Studio
|
# Custom for Visual Studio
|
||||||
*.cs diff=csharp
|
*.cs diff=csharp
|
||||||
|
|
37
build.sh
37
build.sh
|
@ -267,13 +267,23 @@ PackageMacOS()
|
||||||
rm -rf $outputFolderMacOS
|
rm -rf $outputFolderMacOS
|
||||||
mkdir $outputFolderMacOS
|
mkdir $outputFolderMacOS
|
||||||
|
|
||||||
echo "Adding Startup script"
|
|
||||||
cp ./macOS/Sonarr $outputFolderMacOS
|
|
||||||
dos2unix $outputFolderMacOS/Sonarr
|
|
||||||
|
|
||||||
echo "Copying Binaries"
|
echo "Copying Binaries"
|
||||||
cp -r $outputFolderLinux/* $outputFolderMacOS
|
cp -r $outputFolderLinux/* $outputFolderMacOS
|
||||||
|
|
||||||
|
echo "Adding Sonarr Launcher"
|
||||||
|
cp ./distribution/osx/Launcher/dist/Launcher $outputFolderMacOS/
|
||||||
|
mv $outputFolderMacOS/Sonarr.exe $outputFolderMacOS/Sonarr.exe.bak
|
||||||
|
mv $outputFolderMacOS/Launcher $outputFolderMacOS/Sonarr
|
||||||
|
mv $outputFolderMacOS/Sonarr.exe.bak $outputFolderMacOS/Sonarr.exe
|
||||||
|
chmod +x $outputFolderMacOS/Sonarr
|
||||||
|
|
||||||
|
echo "Adding Sonarr.Update Launcher"
|
||||||
|
cp ./distribution/osx/Launcher/dist/Launcher $outputFolderMacOS/Sonarr.Update/
|
||||||
|
mv $outputFolderMacOS/Sonarr.Update/Sonarr.Update.exe $outputFolderMacOS/Sonarr.Update/Sonarr.Update.exe.bak
|
||||||
|
mv $outputFolderMacOS/Sonarr.Update/Launcher $outputFolderMacOS/Sonarr.Update/Sonarr.Update
|
||||||
|
mv $outputFolderMacOS/Sonarr.Update/Sonarr.Update.exe.bak $outputFolderMacOS/Sonarr.Update/Sonarr.Update.exe
|
||||||
|
chmod +x $outputFolderMacOS/Sonarr.Update/Sonarr.Update
|
||||||
|
|
||||||
echo "Adding sqlite dylibs"
|
echo "Adding sqlite dylibs"
|
||||||
cp $sourceFolder/Libraries/Sqlite/*.dylib $outputFolderMacOS
|
cp $sourceFolder/Libraries/Sqlite/*.dylib $outputFolderMacOS
|
||||||
|
|
||||||
|
@ -289,24 +299,27 @@ PackageMacOSApp()
|
||||||
|
|
||||||
rm -rf $outputFolderMacOSApp
|
rm -rf $outputFolderMacOSApp
|
||||||
mkdir $outputFolderMacOSApp
|
mkdir $outputFolderMacOSApp
|
||||||
cp -r ./macOS/Sonarr.app $outputFolderMacOSApp
|
cp -r ./distribution/osx/Sonarr.app $outputFolderMacOSApp
|
||||||
mkdir -p $outputFolderMacOSApp/Sonarr.app/Contents/MacOS
|
mkdir -p $outputFolderMacOSApp/Sonarr.app/Contents/MacOS
|
||||||
|
|
||||||
echo "Adding Startup script"
|
echo "Adding Sonarr Launcher"
|
||||||
cp ./macOS/Sonarr $outputFolderMacOSApp/Sonarr.app/Contents/MacOS
|
cp ./distribution/osx/Launcher/dist/Launcher $outputFolderMacOSApp/Sonarr.app/Contents/MacOS/
|
||||||
dos2unix $outputFolderMacOSApp/Sonarr.app/Contents/MacOS/Sonarr
|
mv $outputFolderMacOSApp/Sonarr.app/Contents/MacOS/Launcher $outputFolderMacOSApp/Sonarr.app/Contents/MacOS/Sonarr
|
||||||
|
chmod +x $outputFolderMacOSApp/Sonarr.app/Contents/MacOS/Sonarr
|
||||||
|
|
||||||
echo "Copying Binaries"
|
echo "Copying Binaries"
|
||||||
cp -r $outputFolderLinux/* $outputFolderMacOSApp/Sonarr.app/Contents/MacOS
|
mkdir -p $outputFolderMacOSApp/Sonarr.app/Contents/MacOS/bin
|
||||||
|
cp -r $outputFolderLinux/* $outputFolderMacOSApp/Sonarr.app/Contents/MacOS/bin/
|
||||||
|
|
||||||
echo "Adding sqlite dylibs"
|
echo "Adding sqlite dylibs"
|
||||||
cp $sourceFolder/Libraries/Sqlite/*.dylib $outputFolderMacOSApp/Sonarr.app/Contents/MacOS
|
cp $sourceFolder/Libraries/Sqlite/*.dylib $outputFolderMacOSApp/Sonarr.app/Contents/MacOS/bin/
|
||||||
|
|
||||||
echo "Adding MediaInfo dylib"
|
echo "Adding MediaInfo dylib"
|
||||||
cp $sourceFolder/Libraries/MediaInfo/*.dylib $outputFolderMacOSApp/Sonarr.app/Contents/MacOS
|
cp $sourceFolder/Libraries/MediaInfo/*.dylib $outputFolderMacOSApp/Sonarr.app/Contents/MacOS/bin/
|
||||||
|
|
||||||
echo "Removing Update Folder"
|
echo "Removing Update Folder"
|
||||||
rm -r $outputFolderMacOSApp/Sonarr.app/Contents/MacOS/Sonarr.Update
|
rm -r $outputFolderMacOSApp/Sonarr.app/Contents/MacOS/bin/Sonarr.Update
|
||||||
|
echo "# Do Not Edit\nPackageVersion=${BUILD_NUMBER}\nPackageAuthor=[Team Sonarr](https://sonarr.tv)\nReleaseVersion=${BUILD_NUMBER}\nUpdateMethod=$PackageUpdater\nBranch=${Branch:-master}" > $outputFolderMacOSApp/Sonarr.app/Contents/MacOS/package_info
|
||||||
|
|
||||||
ProgressEnd 'Creating macOS App Package'
|
ProgressEnd 'Creating macOS App Package'
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ echo Updating changelog for $BuildVersion
|
||||||
sed -i "s:{version}:$BuildVersion:g; s:{branch}:$BuildBranch:g;" debian/changelog
|
sed -i "s:{version}:$BuildVersion:g; s:{branch}:$BuildBranch:g;" debian/changelog
|
||||||
sed -i "s:{version}:$BuildVersion:g; s:{updater}:$PackageUpdater:g" debian/preinst debian/postinst debian/postrm
|
sed -i "s:{version}:$BuildVersion:g; s:{updater}:$PackageUpdater:g" debian/preinst debian/postinst debian/postrm
|
||||||
sed -i '/#BEGIN BUILTIN UPDATER/,/#END BUILTIN UPDATER/d' debian/preinst debian/postinst debian/postrm
|
sed -i '/#BEGIN BUILTIN UPDATER/,/#END BUILTIN UPDATER/d' debian/preinst debian/postinst debian/postrm
|
||||||
echo "# Do Not Edit\nPackageVersion=$BuildVersion\nReleaseVersion=$BuildVersion\nUpdateMethod=$PackageUpdater\nBranch=$BuildBranch" > package_info
|
echo "# Do Not Edit\nPackageVersion=$BuildVersion\nPackageAuthor=[Team Sonarr](https://sonarr.tv)\nReleaseVersion=$BuildVersion\nUpdateMethod=$PackageUpdater\nBranch=$BuildBranch" > package_info
|
||||||
|
|
||||||
echo Running debuild for $BuildVersion
|
echo Running debuild for $BuildVersion
|
||||||
if [ -z "${TEST_OUTPUT}" ]; then
|
if [ -z "${TEST_OUTPUT}" ]; then
|
||||||
|
@ -33,7 +33,7 @@ echo Updating changelog for $BootstrapVersion
|
||||||
sed -i "s:{version}:$BootstrapVersion:g; s:{branch}:$BuildBranch:g;" debian/changelog
|
sed -i "s:{version}:$BootstrapVersion:g; s:{branch}:$BuildBranch:g;" debian/changelog
|
||||||
sed -i "s:{version}:$BuildVersion:g; s:{updater}:$BootstrapUpdater:g" debian/preinst debian/postinst debian/postrm
|
sed -i "s:{version}:$BuildVersion:g; s:{updater}:$BootstrapUpdater:g" debian/preinst debian/postinst debian/postrm
|
||||||
sed -i '/#BEGIN BUILTIN UPDATER/d; /#END BUILTIN UPDATER/d' debian/preinst debian/postinst debian/postrm
|
sed -i '/#BEGIN BUILTIN UPDATER/d; /#END BUILTIN UPDATER/d' debian/preinst debian/postinst debian/postrm
|
||||||
echo "# Do Not Edit\nPackageVersion=$BootstrapVersion\nReleaseVersion=$BuildVersion\nUpdateMethod=$BootstrapUpdater\nBranch=$BuildBranch" > package_info
|
echo "# Do Not Edit\nPackageVersion=$BootstrapVersion\nPackageAuthor=[Team Sonarr](https://sonarr.tv)\nReleaseVersion=$BuildVersion\nUpdateMethod=$BootstrapUpdater\nBranch=$BuildBranch" > package_info
|
||||||
|
|
||||||
echo Running debuild for $BootstrapVersion
|
echo Running debuild for $BootstrapVersion
|
||||||
if [ -z "${TEST_OUTPUT}" ]; then
|
if [ -z "${TEST_OUTPUT}" ]; then
|
||||||
|
|
459
distribution/osx/Launcher/LICENSE.LGPL.md
Normal file
459
distribution/osx/Launcher/LICENSE.LGPL.md
Normal file
|
@ -0,0 +1,459 @@
|
||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
Version 2.1, February 1999
|
||||||
|
|
||||||
|
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
[This is the first released version of the Lesser GPL. It also counts
|
||||||
|
as the successor of the GNU Library Public License, version 2, hence
|
||||||
|
the version number 2.1.]
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your
|
||||||
|
freedom to share and change it. By contrast, the GNU General Public
|
||||||
|
Licenses are intended to guarantee your freedom to share and change
|
||||||
|
free software--to make sure the software is free for all its users.
|
||||||
|
|
||||||
|
This license, the Lesser General Public License, applies to some
|
||||||
|
specially designated software packages--typically libraries--of the
|
||||||
|
Free Software Foundation and other authors who decide to use it. You
|
||||||
|
can use it too, but we suggest you first think carefully about whether
|
||||||
|
this license or the ordinary General Public License is the better
|
||||||
|
strategy to use in any particular case, based on the explanations below.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom of use,
|
||||||
|
not price. Our General Public Licenses are designed to make sure that
|
||||||
|
you have the freedom to distribute copies of free software (and charge
|
||||||
|
for this service if you wish); that you receive source code or can get
|
||||||
|
it if you want it; that you can change the software and use pieces of
|
||||||
|
it in new free programs; and that you are informed that you can do
|
||||||
|
these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid
|
||||||
|
distributors to deny you these rights or to ask you to surrender these
|
||||||
|
rights. These restrictions translate to certain responsibilities for
|
||||||
|
you if you distribute copies of the library or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of the library, whether gratis
|
||||||
|
or for a fee, you must give the recipients all the rights that we gave
|
||||||
|
you. You must make sure that they, too, receive or can get the source
|
||||||
|
code. If you link other code with the library, you must provide
|
||||||
|
complete object files to the recipients, so that they can relink them
|
||||||
|
with the library after making changes to the library and recompiling
|
||||||
|
it. And you must show them these terms so they know their rights.
|
||||||
|
|
||||||
|
We protect your rights with a two-step method: (1) we copyright the
|
||||||
|
library, and (2) we offer you this license, which gives you legal
|
||||||
|
permission to copy, distribute and/or modify the library.
|
||||||
|
|
||||||
|
To protect each distributor, we want to make it very clear that
|
||||||
|
there is no warranty for the free library. Also, if the library is
|
||||||
|
modified by someone else and passed on, the recipients should know
|
||||||
|
that what they have is not the original version, so that the original
|
||||||
|
author's reputation will not be affected by problems that might be
|
||||||
|
introduced by others.
|
||||||
|
|
||||||
|
Finally, software patents pose a constant threat to the existence of
|
||||||
|
any free program. We wish to make sure that a company cannot
|
||||||
|
effectively restrict the users of a free program by obtaining a
|
||||||
|
restrictive license from a patent holder. Therefore, we insist that
|
||||||
|
any patent license obtained for a version of the library must be
|
||||||
|
consistent with the full freedom of use specified in this license.
|
||||||
|
|
||||||
|
Most GNU software, including some libraries, is covered by the
|
||||||
|
ordinary GNU General Public License. This license, the GNU Lesser
|
||||||
|
General Public License, applies to certain designated libraries, and
|
||||||
|
is quite different from the ordinary General Public License. We use
|
||||||
|
this license for certain libraries in order to permit linking those
|
||||||
|
libraries into non-free programs.
|
||||||
|
|
||||||
|
When a program is linked with a library, whether statically or using
|
||||||
|
a shared library, the combination of the two is legally speaking a
|
||||||
|
combined work, a derivative of the original library. The ordinary
|
||||||
|
General Public License therefore permits such linking only if the
|
||||||
|
entire combination fits its criteria of freedom. The Lesser General
|
||||||
|
Public License permits more lax criteria for linking other code with
|
||||||
|
the library.
|
||||||
|
|
||||||
|
We call this license the "Lesser" General Public License because it
|
||||||
|
does Less to protect the user's freedom than the ordinary General
|
||||||
|
Public License. It also provides other free software developers Less
|
||||||
|
of an advantage over competing non-free programs. These disadvantages
|
||||||
|
are the reason we use the ordinary General Public License for many
|
||||||
|
libraries. However, the Lesser license provides advantages in certain
|
||||||
|
special circumstances.
|
||||||
|
|
||||||
|
For example, on rare occasions, there may be a special need to
|
||||||
|
encourage the widest possible use of a certain library, so that it becomes
|
||||||
|
a de-facto standard. To achieve this, non-free programs must be
|
||||||
|
allowed to use the library. A more frequent case is that a free
|
||||||
|
library does the same job as widely used non-free libraries. In this
|
||||||
|
case, there is little to gain by limiting the free library to free
|
||||||
|
software only, so we use the Lesser General Public License.
|
||||||
|
|
||||||
|
In other cases, permission to use a particular library in non-free
|
||||||
|
programs enables a greater number of people to use a large body of
|
||||||
|
free software. For example, permission to use the GNU C Library in
|
||||||
|
non-free programs enables many more people to use the whole GNU
|
||||||
|
operating system, as well as its variant, the GNU/Linux operating
|
||||||
|
system.
|
||||||
|
|
||||||
|
Although the Lesser General Public License is Less protective of the
|
||||||
|
users' freedom, it does ensure that the user of a program that is
|
||||||
|
linked with the Library has the freedom and the wherewithal to run
|
||||||
|
that program using a modified version of the Library.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow. Pay close attention to the difference between a
|
||||||
|
"work based on the library" and a "work that uses the library". The
|
||||||
|
former contains code derived from the library, whereas the latter must
|
||||||
|
be combined with the library in order to run.
|
||||||
|
|
||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License Agreement applies to any software library or other
|
||||||
|
program which contains a notice placed by the copyright holder or
|
||||||
|
other authorized party saying it may be distributed under the terms of
|
||||||
|
this Lesser General Public License (also called "this License").
|
||||||
|
Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
A "library" means a collection of software functions and/or data
|
||||||
|
prepared so as to be conveniently linked with application programs
|
||||||
|
(which use some of those functions and data) to form executables.
|
||||||
|
|
||||||
|
The "Library", below, refers to any such software library or work
|
||||||
|
which has been distributed under these terms. A "work based on the
|
||||||
|
Library" means either the Library or any derivative work under
|
||||||
|
copyright law: that is to say, a work containing the Library or a
|
||||||
|
portion of it, either verbatim or with modifications and/or translated
|
||||||
|
straightforwardly into another language. (Hereinafter, translation is
|
||||||
|
included without limitation in the term "modification".)
|
||||||
|
|
||||||
|
"Source code" for a work means the preferred form of the work for
|
||||||
|
making modifications to it. For a library, complete source code means
|
||||||
|
all the source code for all modules it contains, plus any associated
|
||||||
|
interface definition files, plus the scripts used to control compilation
|
||||||
|
and installation of the library.
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not
|
||||||
|
covered by this License; they are outside its scope. The act of
|
||||||
|
running a program using the Library is not restricted, and output from
|
||||||
|
such a program is covered only if its contents constitute a work based
|
||||||
|
on the Library (independent of the use of the Library in a tool for
|
||||||
|
writing it). Whether that is true depends on what the Library does
|
||||||
|
and what the program that uses the Library does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Library's
|
||||||
|
complete source code as you receive it, in any medium, provided that
|
||||||
|
you conspicuously and appropriately publish on each copy an
|
||||||
|
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||||
|
all the notices that refer to this License and to the absence of any
|
||||||
|
warranty; and distribute a copy of this License along with the
|
||||||
|
Library.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy,
|
||||||
|
and you may at your option offer warranty protection in exchange for a
|
||||||
|
fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Library or any portion
|
||||||
|
of it, thus forming a work based on the Library, and copy and
|
||||||
|
distribute such modifications or work under the terms of Section 1
|
||||||
|
above, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The modified work must itself be a software library.
|
||||||
|
|
||||||
|
b) You must cause the files modified to carry prominent notices
|
||||||
|
stating that you changed the files and the date of any change.
|
||||||
|
|
||||||
|
c) You must cause the whole of the work to be licensed at no
|
||||||
|
charge to all third parties under the terms of this License.
|
||||||
|
|
||||||
|
d) If a facility in the modified Library refers to a function or a
|
||||||
|
table of data to be supplied by an application program that uses
|
||||||
|
the facility, other than as an argument passed when the facility
|
||||||
|
is invoked, then you must make a good faith effort to ensure that,
|
||||||
|
in the event an application does not supply such function or
|
||||||
|
table, the facility still operates, and performs whatever part of
|
||||||
|
its purpose remains meaningful.
|
||||||
|
|
||||||
|
(For example, a function in a library to compute square roots has
|
||||||
|
a purpose that is entirely well-defined independent of the
|
||||||
|
application. Therefore, Subsection 2d requires that any
|
||||||
|
application-supplied function or table used by this function must
|
||||||
|
be optional: if the application does not supply it, the square
|
||||||
|
root function must still compute square roots.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If
|
||||||
|
identifiable sections of that work are not derived from the Library,
|
||||||
|
and can be reasonably considered independent and separate works in
|
||||||
|
themselves, then this License, and its terms, do not apply to those
|
||||||
|
sections when you distribute them as separate works. But when you
|
||||||
|
distribute the same sections as part of a whole which is a work based
|
||||||
|
on the Library, the distribution of the whole must be on the terms of
|
||||||
|
this License, whose permissions for other licensees extend to the
|
||||||
|
entire whole, and thus to each and every part regardless of who wrote
|
||||||
|
it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest
|
||||||
|
your rights to work written entirely by you; rather, the intent is to
|
||||||
|
exercise the right to control the distribution of derivative or
|
||||||
|
collective works based on the Library.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Library
|
||||||
|
with the Library (or with a work based on the Library) on a volume of
|
||||||
|
a storage or distribution medium does not bring the other work under
|
||||||
|
the scope of this License.
|
||||||
|
|
||||||
|
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||||
|
License instead of this License to a given copy of the Library. To do
|
||||||
|
this, you must alter all the notices that refer to this License, so
|
||||||
|
that they refer to the ordinary GNU General Public License, version 2,
|
||||||
|
instead of to this License. (If a newer version than version 2 of the
|
||||||
|
ordinary GNU General Public License has appeared, then you can specify
|
||||||
|
that version instead if you wish.) Do not make any other change in
|
||||||
|
these notices.
|
||||||
|
|
||||||
|
Once this change is made in a given copy, it is irreversible for
|
||||||
|
that copy, so the ordinary GNU General Public License applies to all
|
||||||
|
subsequent copies and derivative works made from that copy.
|
||||||
|
|
||||||
|
This option is useful when you wish to copy part of the code of
|
||||||
|
the Library into a program that is not a library.
|
||||||
|
|
||||||
|
4. You may copy and distribute the Library (or a portion or
|
||||||
|
derivative of it, under Section 2) in object code or executable form
|
||||||
|
under the terms of Sections 1 and 2 above provided that you accompany
|
||||||
|
it with the complete corresponding machine-readable source code, which
|
||||||
|
must be distributed under the terms of Sections 1 and 2 above on a
|
||||||
|
medium customarily used for software interchange.
|
||||||
|
|
||||||
|
If distribution of object code is made by offering access to copy
|
||||||
|
from a designated place, then offering equivalent access to copy the
|
||||||
|
source code from the same place satisfies the requirement to
|
||||||
|
distribute the source code, even though third parties are not
|
||||||
|
compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
5. A program that contains no derivative of any portion of the
|
||||||
|
Library, but is designed to work with the Library by being compiled or
|
||||||
|
linked with it, is called a "work that uses the Library". Such a
|
||||||
|
work, in isolation, is not a derivative work of the Library, and
|
||||||
|
therefore falls outside the scope of this License.
|
||||||
|
|
||||||
|
However, linking a "work that uses the Library" with the Library
|
||||||
|
creates an executable that is a derivative of the Library (because it
|
||||||
|
contains portions of the Library), rather than a "work that uses the
|
||||||
|
library". The executable is therefore covered by this License.
|
||||||
|
Section 6 states terms for distribution of such executables.
|
||||||
|
|
||||||
|
When a "work that uses the Library" uses material from a header file
|
||||||
|
that is part of the Library, the object code for the work may be a
|
||||||
|
derivative work of the Library even though the source code is not.
|
||||||
|
Whether this is true is especially significant if the work can be
|
||||||
|
linked without the Library, or if the work is itself a library. The
|
||||||
|
threshold for this to be true is not precisely defined by law.
|
||||||
|
|
||||||
|
If such an object file uses only numerical parameters, data
|
||||||
|
structure layouts and accessors, and small macros and small inline
|
||||||
|
functions (ten lines or less in length), then the use of the object
|
||||||
|
file is unrestricted, regardless of whether it is legally a derivative
|
||||||
|
work. (Executables containing this object code plus portions of the
|
||||||
|
Library will still fall under Section 6.)
|
||||||
|
|
||||||
|
Otherwise, if the work is a derivative of the Library, you may
|
||||||
|
distribute the object code for the work under the terms of Section 6.
|
||||||
|
Any executables containing that work also fall under Section 6,
|
||||||
|
whether or not they are linked directly with the Library itself.
|
||||||
|
|
||||||
|
6. As an exception to the Sections above, you may also combine or
|
||||||
|
link a "work that uses the Library" with the Library to produce a
|
||||||
|
work containing portions of the Library, and distribute that work
|
||||||
|
under terms of your choice, provided that the terms permit
|
||||||
|
modification of the work for the customer's own use and reverse
|
||||||
|
engineering for debugging such modifications.
|
||||||
|
|
||||||
|
You must give prominent notice with each copy of the work that the
|
||||||
|
Library is used in it and that the Library and its use are covered by
|
||||||
|
this License. You must supply a copy of this License. If the work
|
||||||
|
during execution displays copyright notices, you must include the
|
||||||
|
copyright notice for the Library among them, as well as a reference
|
||||||
|
directing the user to the copy of this License. Also, you must do one
|
||||||
|
of these things:
|
||||||
|
|
||||||
|
a) Accompany the work with the complete corresponding
|
||||||
|
machine-readable source code for the Library including whatever
|
||||||
|
changes were used in the work (which must be distributed under
|
||||||
|
Sections 1 and 2 above); and, if the work is an executable linked
|
||||||
|
with the Library, with the complete machine-readable "work that
|
||||||
|
uses the Library", as object code and/or source code, so that the
|
||||||
|
user can modify the Library and then relink to produce a modified
|
||||||
|
executable containing the modified Library. (It is understood
|
||||||
|
that the user who changes the contents of definitions files in the
|
||||||
|
Library will not necessarily be able to recompile the application
|
||||||
|
to use the modified definitions.)
|
||||||
|
|
||||||
|
b) Use a suitable shared library mechanism for linking with the
|
||||||
|
Library. A suitable mechanism is one that (1) uses at run time a
|
||||||
|
copy of the library already present on the user's computer system,
|
||||||
|
rather than copying library functions into the executable, and (2)
|
||||||
|
will operate properly with a modified version of the library, if
|
||||||
|
the user installs one, as long as the modified version is
|
||||||
|
interface-compatible with the version that the work was made with.
|
||||||
|
|
||||||
|
c) Accompany the work with a written offer, valid for at
|
||||||
|
least three years, to give the same user the materials
|
||||||
|
specified in Subsection 6a, above, for a charge no more
|
||||||
|
than the cost of performing this distribution.
|
||||||
|
|
||||||
|
d) If distribution of the work is made by offering access to copy
|
||||||
|
from a designated place, offer equivalent access to copy the above
|
||||||
|
specified materials from the same place.
|
||||||
|
|
||||||
|
e) Verify that the user has already received a copy of these
|
||||||
|
materials or that you have already sent this user a copy.
|
||||||
|
|
||||||
|
For an executable, the required form of the "work that uses the
|
||||||
|
Library" must include any data and utility programs needed for
|
||||||
|
reproducing the executable from it. However, as a special exception,
|
||||||
|
the materials to be distributed need not include anything that is
|
||||||
|
normally distributed (in either source or binary form) with the major
|
||||||
|
components (compiler, kernel, and so on) of the operating system on
|
||||||
|
which the executable runs, unless that component itself accompanies
|
||||||
|
the executable.
|
||||||
|
|
||||||
|
It may happen that this requirement contradicts the license
|
||||||
|
restrictions of other proprietary libraries that do not normally
|
||||||
|
accompany the operating system. Such a contradiction means you cannot
|
||||||
|
use both them and the Library together in an executable that you
|
||||||
|
distribute.
|
||||||
|
|
||||||
|
7. You may place library facilities that are a work based on the
|
||||||
|
Library side-by-side in a single library together with other library
|
||||||
|
facilities not covered by this License, and distribute such a combined
|
||||||
|
library, provided that the separate distribution of the work based on
|
||||||
|
the Library and of the other library facilities is otherwise
|
||||||
|
permitted, and provided that you do these two things:
|
||||||
|
|
||||||
|
a) Accompany the combined library with a copy of the same work
|
||||||
|
based on the Library, uncombined with any other library
|
||||||
|
facilities. This must be distributed under the terms of the
|
||||||
|
Sections above.
|
||||||
|
|
||||||
|
b) Give prominent notice with the combined library of the fact
|
||||||
|
that part of it is a work based on the Library, and explaining
|
||||||
|
where to find the accompanying uncombined form of the same work.
|
||||||
|
|
||||||
|
8. You may not copy, modify, sublicense, link with, or distribute
|
||||||
|
the Library except as expressly provided under this License. Any
|
||||||
|
attempt otherwise to copy, modify, sublicense, link with, or
|
||||||
|
distribute the Library is void, and will automatically terminate your
|
||||||
|
rights under this License. However, parties who have received copies,
|
||||||
|
or rights, from you under this License will not have their licenses
|
||||||
|
terminated so long as such parties remain in full compliance.
|
||||||
|
|
||||||
|
9. You are not required to accept this License, since you have not
|
||||||
|
signed it. However, nothing else grants you permission to modify or
|
||||||
|
distribute the Library or its derivative works. These actions are
|
||||||
|
prohibited by law if you do not accept this License. Therefore, by
|
||||||
|
modifying or distributing the Library (or any work based on the
|
||||||
|
Library), you indicate your acceptance of this License to do so, and
|
||||||
|
all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Library or works based on it.
|
||||||
|
|
||||||
|
10. Each time you redistribute the Library (or any work based on the
|
||||||
|
Library), the recipient automatically receives a license from the
|
||||||
|
original licensor to copy, distribute, link with or modify the Library
|
||||||
|
subject to these terms and conditions. You may not impose any further
|
||||||
|
restrictions on the recipients' exercise of the rights granted herein.
|
||||||
|
You are not responsible for enforcing compliance by third parties with
|
||||||
|
this License.
|
||||||
|
|
||||||
|
11. If, as a consequence of a court judgment or allegation of patent
|
||||||
|
infringement or for any other reason (not limited to patent issues),
|
||||||
|
conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot
|
||||||
|
distribute so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you
|
||||||
|
may not distribute the Library at all. For example, if a patent
|
||||||
|
license would not permit royalty-free redistribution of the Library by
|
||||||
|
all those who receive copies directly or indirectly through you, then
|
||||||
|
the only way you could satisfy both it and this License would be to
|
||||||
|
refrain entirely from distribution of the Library.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under any
|
||||||
|
particular circumstance, the balance of the section is intended to apply,
|
||||||
|
and the section as a whole is intended to apply in other circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any
|
||||||
|
patents or other property right claims or to contest validity of any
|
||||||
|
such claims; this section has the sole purpose of protecting the
|
||||||
|
integrity of the free software distribution system which is
|
||||||
|
implemented by public license practices. Many people have made
|
||||||
|
generous contributions to the wide range of software distributed
|
||||||
|
through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing
|
||||||
|
to distribute software through any other system and a licensee cannot
|
||||||
|
impose that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to
|
||||||
|
be a consequence of the rest of this License.
|
||||||
|
|
||||||
|
12. If the distribution and/or use of the Library is restricted in
|
||||||
|
certain countries either by patents or by copyrighted interfaces, the
|
||||||
|
original copyright holder who places the Library under this License may add
|
||||||
|
an explicit geographical distribution limitation excluding those countries,
|
||||||
|
so that distribution is permitted only in or among countries not thus
|
||||||
|
excluded. In such case, this License incorporates the limitation as if
|
||||||
|
written in the body of this License.
|
||||||
|
|
||||||
|
13. The Free Software Foundation may publish revised and/or new
|
||||||
|
versions of the Lesser General Public License from time to time.
|
||||||
|
Such new versions will be similar in spirit to the present version,
|
||||||
|
but may differ in detail to address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the Library
|
||||||
|
specifies a version number of this License which applies to it and
|
||||||
|
"any later version", you have the option of following the terms and
|
||||||
|
conditions either of that version or of any later version published by
|
||||||
|
the Free Software Foundation. If the Library does not specify a
|
||||||
|
license version number, you may choose any version ever published by
|
||||||
|
the Free Software Foundation.
|
||||||
|
|
||||||
|
14. If you wish to incorporate parts of the Library into other free
|
||||||
|
programs whose distribution conditions are incompatible with these,
|
||||||
|
write to the author to ask for permission. For software which is
|
||||||
|
copyrighted by the Free Software Foundation, write to the Free
|
||||||
|
Software Foundation; we sometimes make exceptions for this. Our
|
||||||
|
decision will be guided by the two goals of preserving the free status
|
||||||
|
of all derivatives of our free software and of promoting the sharing
|
||||||
|
and reuse of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||||
|
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||||
|
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||||
|
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||||
|
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||||
|
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||||
|
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||||
|
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||||
|
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||||
|
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||||
|
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||||
|
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||||
|
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||||
|
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||||
|
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||||
|
DAMAGES.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
28
distribution/osx/Launcher/README.md
Normal file
28
distribution/osx/Launcher/README.md
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
Code reused from duplicati, licensed under LGPL 2.1
|
||||||
|
Modified for Sonarr by Taloth Saldono
|
||||||
|
|
||||||
|
see here for the original source: https://github.com/duplicati/duplicati/tree/679981d29f8a6e445d3c1e6d41e72a673ffaa653/Installer/OSX
|
||||||
|
|
||||||
|
License
|
||||||
|
-------
|
||||||
|
|
||||||
|
Sonarr as a whole is licensed under GPL 3.0 as specified in the git repository root.
|
||||||
|
|
||||||
|
But to preserve the original intent of the duplicati project, the modified versions of the sources in this folder are dual licensed under LGPL 2.1 and GPL 3.0.
|
||||||
|
Note: This exception can be freely removed in any copy of Sonarr sources as per LGPL/GPL licensing terms.
|
||||||
|
|
||||||
|
A copy of the LGPL 2.1 license is included in the LICENSE.LGPL.md file.
|
||||||
|
|
||||||
|
Purpose
|
||||||
|
-------
|
||||||
|
|
||||||
|
The Launcher is a bootstrap/shim application that checks if the appropriate version of mono is installed and subsequently use it to execute Sonarr.
|
||||||
|
By using a separate application, instead of a shell script, this allows the user to assign certain operating system permissions to Sonarr specifically.
|
||||||
|
|
||||||
|
Compiling the Launcher
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
You need an OSX installation with xcode
|
||||||
|
Then run compile.sh in a terminal
|
||||||
|
|
||||||
|
The generated dist/Launcher can be renamed to Sonarr and Sonarr.Update to serve as shims to run Sonarr.exe and Sonarr.Update.exe respectively.
|
BIN
distribution/osx/Launcher/dist/Launcher
vendored
Executable file
BIN
distribution/osx/Launcher/dist/Launcher
vendored
Executable file
Binary file not shown.
32
distribution/osx/Launcher/src/Launcher.m
Normal file
32
distribution/osx/Launcher/src/Launcher.m
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#import "run-with-mono.h"
|
||||||
|
#import "PFMoveApplication.h"
|
||||||
|
|
||||||
|
int const MONO_VERSION_MAJOR = 5;
|
||||||
|
int const MONO_VERSION_MINOR = 20;
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
@autoreleasepool {
|
||||||
|
// Use our own executable name so the same compiled binary to be used for forks
|
||||||
|
NSString * const FileName = NSProcessInfo.processInfo.arguments[0].lastPathComponent;
|
||||||
|
|
||||||
|
// Sonarr.Update.exe
|
||||||
|
NSString * const ASSEMBLY = [NSString stringWithFormat:@"%@.exe", FileName];
|
||||||
|
|
||||||
|
// Sonarr Update
|
||||||
|
NSString * const APP_NAME = [FileName stringByReplacingOccurrencesOfString:@"." withString:@" "];
|
||||||
|
|
||||||
|
// -sonarrupdate
|
||||||
|
NSString * const PROCESS_NAME = [NSString stringWithFormat:@"-%@", [FileName stringByReplacingOccurrencesOfString:@"." withString:@""].lowercaseString];
|
||||||
|
|
||||||
|
@try
|
||||||
|
{
|
||||||
|
PFMoveToApplicationsFolderIfNecessary();
|
||||||
|
}
|
||||||
|
@catch (NSException * ex)
|
||||||
|
{
|
||||||
|
NSLog(@"Translocation/Quarantine check failed, starting normally. Reason: %@", ex.reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [RunWithMono runAssemblyWithMono:APP_NAME procnamesuffix:PROCESS_NAME assembly:ASSEMBLY major:MONO_VERSION_MAJOR minor:MONO_VERSION_MINOR];
|
||||||
|
}
|
||||||
|
}
|
32
distribution/osx/Launcher/src/PFMoveApplication.h
Normal file
32
distribution/osx/Launcher/src/PFMoveApplication.h
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
//
|
||||||
|
// PFMoveApplication.h, version 1.24
|
||||||
|
// LetsMove
|
||||||
|
//
|
||||||
|
// Created by Andy Kim at Potion Factory LLC on 9/17/09
|
||||||
|
//
|
||||||
|
// The contents of this file are dedicated to the public domain.
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
Moves the running application to ~/Applications or /Applications if the former does not exist.
|
||||||
|
After the move, it relaunches app from the new location.
|
||||||
|
DOES NOT work for sandboxed applications.
|
||||||
|
|
||||||
|
Call from \c NSApplication's delegate method \c -applicationWillFinishLaunching: method. */
|
||||||
|
void PFMoveToApplicationsFolderIfNecessary(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Check whether an app move is currently in progress.
|
||||||
|
Returns YES if LetsMove is currently in-progress trying to move the app to the Applications folder, or NO otherwise.
|
||||||
|
This can be used to work around a crash with apps that terminate after last window is closed.
|
||||||
|
See https://github.com/potionfactory/LetsMove/issues/64 for details. */
|
||||||
|
BOOL PFMoveIsInProgress(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
565
distribution/osx/Launcher/src/PFMoveApplication.m
Normal file
565
distribution/osx/Launcher/src/PFMoveApplication.m
Normal file
|
@ -0,0 +1,565 @@
|
||||||
|
//
|
||||||
|
// PFMoveApplication.m, version 1.24
|
||||||
|
// LetsMove
|
||||||
|
//
|
||||||
|
// Created by Andy Kim at Potion Factory LLC on 9/17/09
|
||||||
|
//
|
||||||
|
// The contents of this file are dedicated to the public domain.
|
||||||
|
|
||||||
|
#import "PFMoveApplication.h"
|
||||||
|
|
||||||
|
#import <AppKit/AppKit.h>
|
||||||
|
#import <Security/Security.h>
|
||||||
|
#import <dlfcn.h>
|
||||||
|
#import <sys/mount.h>
|
||||||
|
|
||||||
|
@interface LetsMove : NSObject
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation LetsMove
|
||||||
|
+ (NSBundle *)bundle {
|
||||||
|
return [NSBundle bundleForClass:self];
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
// Strings
|
||||||
|
// These are macros to be able to use custom i18n tools
|
||||||
|
#define _I10NS(nsstr) NSLocalizedStringFromTableInBundle(nsstr, @"MoveApplication", [LetsMove bundle], nil)
|
||||||
|
#define kStrMoveApplicationCouldNotMove _I10NS(@"Could not move to Applications folder")
|
||||||
|
#define kStrMoveApplicationQuestionTitle _I10NS(@"Move to Applications folder?")
|
||||||
|
#define kStrMoveApplicationQuestionTitleHome _I10NS(@"Move to Applications folder in your Home folder?")
|
||||||
|
#define kStrMoveApplicationQuestionMessage _I10NS(@"I can move myself to the Applications folder if you'd like.")
|
||||||
|
#define kStrMoveApplicationButtonMove _I10NS(@"Move to Applications Folder")
|
||||||
|
#define kStrMoveApplicationButtonDoNotMove _I10NS(@"Do Not Move")
|
||||||
|
#define kStrMoveApplicationQuestionInfoWillRequirePasswd _I10NS(@"Note that this will require an administrator password.")
|
||||||
|
#define kStrMoveApplicationQuestionInfoInDownloadsFolder _I10NS(@"This will keep your Downloads folder uncluttered.")
|
||||||
|
|
||||||
|
// Needs to be defined for compiling under 10.5 SDK
|
||||||
|
#ifndef NSAppKitVersionNumber10_5
|
||||||
|
#define NSAppKitVersionNumber10_5 949
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// By default, we use a small control/font for the suppression button.
|
||||||
|
// If you prefer to use the system default (to match your other alerts),
|
||||||
|
// set this to 0.
|
||||||
|
#define PFUseSmallAlertSuppressCheckbox 1
|
||||||
|
|
||||||
|
|
||||||
|
static NSString *AlertSuppressKey = @"moveToApplicationsFolderAlertSuppress";
|
||||||
|
static BOOL MoveInProgress = NO;
|
||||||
|
|
||||||
|
// Helper functions
|
||||||
|
static NSString *PreferredInstallLocation(BOOL *isUserDirectory);
|
||||||
|
static BOOL IsInApplicationsFolder(NSString *path);
|
||||||
|
static BOOL IsInDownloadsFolder(NSString *path);
|
||||||
|
static BOOL IsApplicationAtPathRunning(NSString *path);
|
||||||
|
static BOOL IsApplicationAtPathNested(NSString *path);
|
||||||
|
static NSString *ContainingDiskImageDevice(NSString *path);
|
||||||
|
static BOOL Trash(NSString *path);
|
||||||
|
static BOOL DeleteOrTrash(NSString *path);
|
||||||
|
static BOOL AuthorizedInstall(NSString *srcPath, NSString *dstPath, BOOL *canceled);
|
||||||
|
static BOOL CopyBundle(NSString *srcPath, NSString *dstPath);
|
||||||
|
static NSString *ShellQuotedString(NSString *string);
|
||||||
|
static void Relaunch(NSString *destinationPath);
|
||||||
|
|
||||||
|
// Main worker function
|
||||||
|
void PFMoveToApplicationsFolderIfNecessary(void) {
|
||||||
|
|
||||||
|
// Make sure to do our work on the main thread.
|
||||||
|
// Apparently Electron apps need this for things to work properly.
|
||||||
|
if (![NSThread isMainThread]) {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
PFMoveToApplicationsFolderIfNecessary();
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip if user suppressed the alert before
|
||||||
|
if ([[NSUserDefaults standardUserDefaults] boolForKey:AlertSuppressKey]) return;
|
||||||
|
|
||||||
|
// Path of the bundle
|
||||||
|
NSString *bundlePath = [[NSBundle mainBundle] bundlePath];
|
||||||
|
|
||||||
|
// Check if the bundle is embedded in another application
|
||||||
|
BOOL isNestedApplication = IsApplicationAtPathNested(bundlePath);
|
||||||
|
|
||||||
|
// Skip if the application is already in some Applications folder,
|
||||||
|
// unless it's inside another app's bundle.
|
||||||
|
if (IsInApplicationsFolder(bundlePath) && !isNestedApplication) return;
|
||||||
|
|
||||||
|
// OK, looks like we'll need to do a move - set the status variable appropriately
|
||||||
|
MoveInProgress = YES;
|
||||||
|
|
||||||
|
// File Manager
|
||||||
|
NSFileManager *fm = [NSFileManager defaultManager];
|
||||||
|
|
||||||
|
// Are we on a disk image?
|
||||||
|
NSString *diskImageDevice = ContainingDiskImageDevice(bundlePath);
|
||||||
|
|
||||||
|
// Since we are good to go, get the preferred installation directory.
|
||||||
|
BOOL installToUserApplications = NO;
|
||||||
|
NSString *applicationsDirectory = PreferredInstallLocation(&installToUserApplications);
|
||||||
|
NSString *bundleName = [bundlePath lastPathComponent];
|
||||||
|
NSString *destinationPath = [applicationsDirectory stringByAppendingPathComponent:bundleName];
|
||||||
|
|
||||||
|
// Check if we need admin password to write to the Applications directory
|
||||||
|
BOOL needAuthorization = ([fm isWritableFileAtPath:applicationsDirectory] == NO);
|
||||||
|
|
||||||
|
// Check if the destination bundle is already there but not writable
|
||||||
|
needAuthorization |= ([fm fileExistsAtPath:destinationPath] && ![fm isWritableFileAtPath:destinationPath]);
|
||||||
|
|
||||||
|
// Setup the alert
|
||||||
|
NSAlert *alert = [[[NSAlert alloc] init] autorelease];
|
||||||
|
{
|
||||||
|
NSString *informativeText = nil;
|
||||||
|
|
||||||
|
[alert setMessageText:(installToUserApplications ? kStrMoveApplicationQuestionTitleHome : kStrMoveApplicationQuestionTitle)];
|
||||||
|
|
||||||
|
informativeText = kStrMoveApplicationQuestionMessage;
|
||||||
|
|
||||||
|
if (needAuthorization) {
|
||||||
|
informativeText = [informativeText stringByAppendingString:@" "];
|
||||||
|
informativeText = [informativeText stringByAppendingString:kStrMoveApplicationQuestionInfoWillRequirePasswd];
|
||||||
|
}
|
||||||
|
else if (IsInDownloadsFolder(bundlePath)) {
|
||||||
|
// Don't mention this stuff if we need authentication. The informative text is long enough as it is in that case.
|
||||||
|
informativeText = [informativeText stringByAppendingString:@" "];
|
||||||
|
informativeText = [informativeText stringByAppendingString:kStrMoveApplicationQuestionInfoInDownloadsFolder];
|
||||||
|
}
|
||||||
|
|
||||||
|
[alert setInformativeText:informativeText];
|
||||||
|
|
||||||
|
// Add accept button
|
||||||
|
[alert addButtonWithTitle:kStrMoveApplicationButtonMove];
|
||||||
|
|
||||||
|
// Add deny button
|
||||||
|
NSButton *cancelButton = [alert addButtonWithTitle:kStrMoveApplicationButtonDoNotMove];
|
||||||
|
[cancelButton setKeyEquivalent:[NSString stringWithFormat:@"%C", 0x1b]]; // Escape key
|
||||||
|
|
||||||
|
// Setup suppression button
|
||||||
|
[alert setShowsSuppressionButton:YES];
|
||||||
|
|
||||||
|
if (PFUseSmallAlertSuppressCheckbox) {
|
||||||
|
NSCell *cell = [[alert suppressionButton] cell];
|
||||||
|
[cell setControlSize:NSSmallControlSize];
|
||||||
|
[cell setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Activate app -- work-around for focus issues related to "scary file from internet" OS dialog.
|
||||||
|
if (![NSApp isActive]) {
|
||||||
|
[NSApp activateIgnoringOtherApps:YES];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([alert runModal] == NSAlertFirstButtonReturn) {
|
||||||
|
NSLog(@"INFO -- Moving myself to the Applications folder");
|
||||||
|
|
||||||
|
// Move
|
||||||
|
if (needAuthorization) {
|
||||||
|
BOOL authorizationCanceled;
|
||||||
|
|
||||||
|
if (!AuthorizedInstall(bundlePath, destinationPath, &authorizationCanceled)) {
|
||||||
|
if (authorizationCanceled) {
|
||||||
|
NSLog(@"INFO -- Not moving because user canceled authorization");
|
||||||
|
MoveInProgress = NO;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
NSLog(@"ERROR -- Could not copy myself to /Applications with authorization");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// If a copy already exists in the Applications folder, put it in the Trash
|
||||||
|
if ([fm fileExistsAtPath:destinationPath]) {
|
||||||
|
// But first, make sure that it's not running
|
||||||
|
if (IsApplicationAtPathRunning(destinationPath)) {
|
||||||
|
// Give the running app focus and terminate myself
|
||||||
|
NSLog(@"INFO -- Switching to an already running version");
|
||||||
|
[[NSTask launchedTaskWithLaunchPath:@"/usr/bin/open" arguments:[NSArray arrayWithObject:destinationPath]] waitUntilExit];
|
||||||
|
MoveInProgress = NO;
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!Trash([applicationsDirectory stringByAppendingPathComponent:bundleName]))
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CopyBundle(bundlePath, destinationPath)) {
|
||||||
|
NSLog(@"ERROR -- Could not copy myself to %@", destinationPath);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trash the original app. It's okay if this fails.
|
||||||
|
// NOTE: This final delete does not work if the source bundle is in a network mounted volume.
|
||||||
|
// Calling rm or file manager's delete method doesn't work either. It's unlikely to happen
|
||||||
|
// but it'd be great if someone could fix this.
|
||||||
|
if (!isNestedApplication && diskImageDevice == nil && !DeleteOrTrash(bundlePath)) {
|
||||||
|
NSLog(@"WARNING -- Could not delete application after moving it to Applications folder");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Relaunch.
|
||||||
|
Relaunch(destinationPath);
|
||||||
|
|
||||||
|
// Launched from within a disk image? -- unmount (if no files are open after 5 seconds,
|
||||||
|
// otherwise leave it mounted).
|
||||||
|
if (diskImageDevice && !isNestedApplication) {
|
||||||
|
NSString *script = [NSString stringWithFormat:@"(/bin/sleep 5 && /usr/bin/hdiutil detach %@) &", ShellQuotedString(diskImageDevice)];
|
||||||
|
[NSTask launchedTaskWithLaunchPath:@"/bin/sh" arguments:[NSArray arrayWithObjects:@"-c", script, nil]];
|
||||||
|
}
|
||||||
|
|
||||||
|
MoveInProgress = NO;
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
// Save the alert suppress preference if checked
|
||||||
|
else if ([[alert suppressionButton] state] == NSOnState) {
|
||||||
|
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:AlertSuppressKey];
|
||||||
|
}
|
||||||
|
|
||||||
|
MoveInProgress = NO;
|
||||||
|
return;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
{
|
||||||
|
// Show failure message
|
||||||
|
alert = [[[NSAlert alloc] init] autorelease];
|
||||||
|
[alert setMessageText:kStrMoveApplicationCouldNotMove];
|
||||||
|
[alert runModal];
|
||||||
|
MoveInProgress = NO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL PFMoveIsInProgress() {
|
||||||
|
return MoveInProgress;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark -
|
||||||
|
#pragma mark Helper Functions
|
||||||
|
|
||||||
|
static NSString *PreferredInstallLocation(BOOL *isUserDirectory) {
|
||||||
|
// Return the preferred install location.
|
||||||
|
// Assume that if the user has a ~/Applications folder, they'd prefer their
|
||||||
|
// applications to go there.
|
||||||
|
|
||||||
|
NSFileManager *fm = [NSFileManager defaultManager];
|
||||||
|
|
||||||
|
/*
|
||||||
|
NSArray *userApplicationsDirs = NSSearchPathForDirectoriesInDomains(NSApplicationDirectory, NSUserDomainMask, YES);
|
||||||
|
|
||||||
|
if ([userApplicationsDirs count] > 0) {
|
||||||
|
NSString *userApplicationsDir = [userApplicationsDirs objectAtIndex:0];
|
||||||
|
BOOL isDirectory;
|
||||||
|
|
||||||
|
if ([fm fileExistsAtPath:userApplicationsDir isDirectory:&isDirectory] && isDirectory) {
|
||||||
|
// User Applications directory exists. Get the directory contents.
|
||||||
|
NSArray *contents = [fm contentsOfDirectoryAtPath:userApplicationsDir error:NULL];
|
||||||
|
|
||||||
|
// Check if there is at least one ".app" inside the directory.
|
||||||
|
for (NSString *contentsPath in contents) {
|
||||||
|
if ([[contentsPath pathExtension] isEqualToString:@"app"]) {
|
||||||
|
if (isUserDirectory) *isUserDirectory = YES;
|
||||||
|
return [userApplicationsDir stringByResolvingSymlinksInPath];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// No user Applications directory in use. Return the machine local Applications directory
|
||||||
|
if (isUserDirectory) *isUserDirectory = NO;
|
||||||
|
|
||||||
|
return [[NSSearchPathForDirectoriesInDomains(NSApplicationDirectory, NSLocalDomainMask, YES) lastObject] stringByResolvingSymlinksInPath];
|
||||||
|
}
|
||||||
|
|
||||||
|
static BOOL IsInApplicationsFolder(NSString *path) {
|
||||||
|
// Check all the normal Application directories
|
||||||
|
NSArray *applicationDirs = NSSearchPathForDirectoriesInDomains(NSApplicationDirectory, NSAllDomainsMask, YES);
|
||||||
|
for (NSString *appDir in applicationDirs) {
|
||||||
|
if ([path hasPrefix:appDir]) return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also, handle the case that the user has some other Application directory (perhaps on a separate data partition).
|
||||||
|
if ([[path pathComponents] containsObject:@"Applications"]) return YES;
|
||||||
|
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BOOL IsInDownloadsFolder(NSString *path) {
|
||||||
|
NSArray *downloadDirs = NSSearchPathForDirectoriesInDomains(NSDownloadsDirectory, NSAllDomainsMask, YES);
|
||||||
|
for (NSString *downloadsDirPath in downloadDirs) {
|
||||||
|
if ([path hasPrefix:downloadsDirPath]) return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BOOL IsApplicationAtPathRunning(NSString *bundlePath) {
|
||||||
|
bundlePath = [bundlePath stringByStandardizingPath];
|
||||||
|
|
||||||
|
#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_5
|
||||||
|
// Use the new API on 10.6 or higher to determine if the app is already running
|
||||||
|
if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_5) {
|
||||||
|
for (NSRunningApplication *runningApplication in [[NSWorkspace sharedWorkspace] runningApplications]) {
|
||||||
|
NSString *runningAppBundlePath = [[[runningApplication bundleURL] path] stringByStandardizingPath];
|
||||||
|
if ([runningAppBundlePath isEqualToString:bundlePath]) {
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
// Use the shell to determine if the app is already running on systems 10.5 or lower
|
||||||
|
NSString *script = [NSString stringWithFormat:@"/bin/ps ax -o comm | /usr/bin/grep %@/ | /usr/bin/grep -v grep >/dev/null", ShellQuotedString(bundlePath)];
|
||||||
|
NSTask *task = [NSTask launchedTaskWithLaunchPath:@"/bin/sh" arguments:[NSArray arrayWithObjects:@"-c", script, nil]];
|
||||||
|
[task waitUntilExit];
|
||||||
|
|
||||||
|
// If the task terminated with status 0, it means that the final grep produced 1 or more lines of output.
|
||||||
|
// Which means that the app is already running
|
||||||
|
return [task terminationStatus] == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BOOL IsApplicationAtPathNested(NSString *path) {
|
||||||
|
NSString *containingPath = [path stringByDeletingLastPathComponent];
|
||||||
|
|
||||||
|
NSArray *components = [containingPath pathComponents];
|
||||||
|
for (NSString *component in components) {
|
||||||
|
if ([[component pathExtension] isEqualToString:@"app"]) {
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NSString *ContainingDiskImageDevice(NSString *path) {
|
||||||
|
NSString *containingPath = [path stringByDeletingLastPathComponent];
|
||||||
|
|
||||||
|
struct statfs fs;
|
||||||
|
if (statfs([containingPath fileSystemRepresentation], &fs) || (fs.f_flags & MNT_ROOTFS))
|
||||||
|
return nil;
|
||||||
|
|
||||||
|
NSString *device = [[NSFileManager defaultManager] stringWithFileSystemRepresentation:fs.f_mntfromname length:strlen(fs.f_mntfromname)];
|
||||||
|
|
||||||
|
NSTask *hdiutil = [[[NSTask alloc] init] autorelease];
|
||||||
|
[hdiutil setLaunchPath:@"/usr/bin/hdiutil"];
|
||||||
|
[hdiutil setArguments:[NSArray arrayWithObjects:@"info", @"-plist", nil]];
|
||||||
|
[hdiutil setStandardOutput:[NSPipe pipe]];
|
||||||
|
[hdiutil launch];
|
||||||
|
[hdiutil waitUntilExit];
|
||||||
|
|
||||||
|
NSData *data = [[[hdiutil standardOutput] fileHandleForReading] readDataToEndOfFile];
|
||||||
|
NSDictionary *info = nil;
|
||||||
|
#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_5
|
||||||
|
if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_5) {
|
||||||
|
info = [NSPropertyListSerialization propertyListWithData:data options:NSPropertyListImmutable format:NULL error:NULL];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
#endif
|
||||||
|
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10
|
||||||
|
info = [NSPropertyListSerialization propertyListFromData:data mutabilityOption:NSPropertyListImmutable format:NULL errorDescription:NULL];
|
||||||
|
#endif
|
||||||
|
#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_5
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (![info isKindOfClass:[NSDictionary class]]) return nil;
|
||||||
|
|
||||||
|
NSArray *images = (NSArray *)[info objectForKey:@"images"];
|
||||||
|
if (![images isKindOfClass:[NSArray class]]) return nil;
|
||||||
|
|
||||||
|
for (NSDictionary *image in images) {
|
||||||
|
if (![image isKindOfClass:[NSDictionary class]]) return nil;
|
||||||
|
|
||||||
|
id systemEntities = [image objectForKey:@"system-entities"];
|
||||||
|
if (![systemEntities isKindOfClass:[NSArray class]]) return nil;
|
||||||
|
|
||||||
|
for (NSDictionary *systemEntity in systemEntities) {
|
||||||
|
if (![systemEntity isKindOfClass:[NSDictionary class]]) return nil;
|
||||||
|
|
||||||
|
NSString *devEntry = [systemEntity objectForKey:@"dev-entry"];
|
||||||
|
if (![devEntry isKindOfClass:[NSString class]]) return nil;
|
||||||
|
|
||||||
|
if ([devEntry isEqualToString:device])
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BOOL Trash(NSString *path) {
|
||||||
|
BOOL result = NO;
|
||||||
|
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
|
||||||
|
if (floor(NSAppKitVersionNumber) >= NSAppKitVersionNumber10_8) {
|
||||||
|
result = [[NSFileManager defaultManager] trashItemAtURL:[NSURL fileURLWithPath:path] resultingItemURL:NULL error:NULL];
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_11
|
||||||
|
if (!result) {
|
||||||
|
result = [[NSWorkspace sharedWorkspace] performFileOperation:NSWorkspaceRecycleOperation
|
||||||
|
source:[path stringByDeletingLastPathComponent]
|
||||||
|
destination:@""
|
||||||
|
files:[NSArray arrayWithObject:[path lastPathComponent]]
|
||||||
|
tag:NULL];
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// As a last resort try trashing with AppleScript.
|
||||||
|
// This allows us to trash the app in macOS Sierra even when the app is running inside
|
||||||
|
// an app translocation image.
|
||||||
|
if (!result) {
|
||||||
|
NSAppleScript *appleScript = [[[NSAppleScript alloc] initWithSource:
|
||||||
|
[NSString stringWithFormat:@"\
|
||||||
|
set theFile to POSIX file \"%@\" \n\
|
||||||
|
tell application \"Finder\" \n\
|
||||||
|
move theFile to trash \n\
|
||||||
|
end tell", path]] autorelease];
|
||||||
|
NSDictionary *errorDict = nil;
|
||||||
|
NSAppleEventDescriptor *scriptResult = [appleScript executeAndReturnError:&errorDict];
|
||||||
|
if (scriptResult == nil) {
|
||||||
|
NSLog(@"Trash AppleScript error: %@", errorDict);
|
||||||
|
}
|
||||||
|
result = (scriptResult != nil);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
NSLog(@"ERROR -- Could not trash '%@'", path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BOOL DeleteOrTrash(NSString *path) {
|
||||||
|
NSError *error;
|
||||||
|
|
||||||
|
if ([[NSFileManager defaultManager] removeItemAtPath:path error:&error]) {
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Don't log warning if on Sierra and running inside App Translocation path
|
||||||
|
if ([path rangeOfString:@"/AppTranslocation/"].location == NSNotFound)
|
||||||
|
NSLog(@"WARNING -- Could not delete '%@': %@", path, [error localizedDescription]);
|
||||||
|
|
||||||
|
return Trash(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static BOOL AuthorizedInstall(NSString *srcPath, NSString *dstPath, BOOL *canceled) {
|
||||||
|
if (canceled) *canceled = NO;
|
||||||
|
|
||||||
|
// Make sure that the destination path is an app bundle. We're essentially running 'sudo rm -rf'
|
||||||
|
// so we really don't want to fuck this up.
|
||||||
|
if (![[dstPath pathExtension] isEqualToString:@"app"]) return NO;
|
||||||
|
|
||||||
|
// Do some more checks
|
||||||
|
if ([[dstPath stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] length] == 0) return NO;
|
||||||
|
if ([[srcPath stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] length] == 0) return NO;
|
||||||
|
|
||||||
|
int pid, status;
|
||||||
|
AuthorizationRef myAuthorizationRef;
|
||||||
|
|
||||||
|
// Get the authorization
|
||||||
|
OSStatus err = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &myAuthorizationRef);
|
||||||
|
if (err != errAuthorizationSuccess) return NO;
|
||||||
|
|
||||||
|
AuthorizationItem myItems = {kAuthorizationRightExecute, 0, NULL, 0};
|
||||||
|
AuthorizationRights myRights = {1, &myItems};
|
||||||
|
AuthorizationFlags myFlags = (AuthorizationFlags)(kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights | kAuthorizationFlagPreAuthorize);
|
||||||
|
|
||||||
|
err = AuthorizationCopyRights(myAuthorizationRef, &myRights, NULL, myFlags, NULL);
|
||||||
|
if (err != errAuthorizationSuccess) {
|
||||||
|
if (err == errAuthorizationCanceled && canceled)
|
||||||
|
*canceled = YES;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
static OSStatus (*security_AuthorizationExecuteWithPrivileges)(AuthorizationRef authorization, const char *pathToTool,
|
||||||
|
AuthorizationFlags options, char * const *arguments,
|
||||||
|
FILE **communicationsPipe) = NULL;
|
||||||
|
if (!security_AuthorizationExecuteWithPrivileges) {
|
||||||
|
// On 10.7, AuthorizationExecuteWithPrivileges is deprecated. We want to still use it since there's no
|
||||||
|
// good alternative (without requiring code signing). We'll look up the function through dyld and fail
|
||||||
|
// if it is no longer accessible. If Apple removes the function entirely this will fail gracefully. If
|
||||||
|
// they keep the function and throw some sort of exception, this won't fail gracefully, but that's a
|
||||||
|
// risk we'll have to take for now.
|
||||||
|
security_AuthorizationExecuteWithPrivileges = (OSStatus (*)(AuthorizationRef, const char*,
|
||||||
|
AuthorizationFlags, char* const*,
|
||||||
|
FILE **)) dlsym(RTLD_DEFAULT, "AuthorizationExecuteWithPrivileges");
|
||||||
|
}
|
||||||
|
if (!security_AuthorizationExecuteWithPrivileges) goto fail;
|
||||||
|
|
||||||
|
// Delete the destination
|
||||||
|
{
|
||||||
|
char *args[] = {"-rf", (char *)[dstPath fileSystemRepresentation], NULL};
|
||||||
|
err = security_AuthorizationExecuteWithPrivileges(myAuthorizationRef, "/bin/rm", kAuthorizationFlagDefaults, args, NULL);
|
||||||
|
if (err != errAuthorizationSuccess) goto fail;
|
||||||
|
|
||||||
|
// Wait until it's done
|
||||||
|
pid = wait(&status);
|
||||||
|
if (pid == -1 || !WIFEXITED(status)) goto fail; // We don't care about exit status as the destination most likely does not exist
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy
|
||||||
|
{
|
||||||
|
char *args[] = {"-pR", (char *)[srcPath fileSystemRepresentation], (char *)[dstPath fileSystemRepresentation], NULL};
|
||||||
|
err = security_AuthorizationExecuteWithPrivileges(myAuthorizationRef, "/bin/cp", kAuthorizationFlagDefaults, args, NULL);
|
||||||
|
if (err != errAuthorizationSuccess) goto fail;
|
||||||
|
|
||||||
|
// Wait until it's done
|
||||||
|
pid = wait(&status);
|
||||||
|
if (pid == -1 || !WIFEXITED(status) || WEXITSTATUS(status)) goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
AuthorizationFree(myAuthorizationRef, kAuthorizationFlagDefaults);
|
||||||
|
return YES;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
AuthorizationFree(myAuthorizationRef, kAuthorizationFlagDefaults);
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BOOL CopyBundle(NSString *srcPath, NSString *dstPath) {
|
||||||
|
NSFileManager *fm = [NSFileManager defaultManager];
|
||||||
|
NSError *error = nil;
|
||||||
|
|
||||||
|
if ([fm copyItemAtPath:srcPath toPath:dstPath error:&error]) {
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
NSLog(@"ERROR -- Could not copy '%@' to '%@' (%@)", srcPath, dstPath, error);
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static NSString *ShellQuotedString(NSString *string) {
|
||||||
|
return [NSString stringWithFormat:@"'%@'", [string stringByReplacingOccurrencesOfString:@"'" withString:@"'\\''"]];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Relaunch(NSString *destinationPath) {
|
||||||
|
// The shell script waits until the original app process terminates.
|
||||||
|
// This is done so that the relaunched app opens as the front-most app.
|
||||||
|
int pid = [[NSProcessInfo processInfo] processIdentifier];
|
||||||
|
|
||||||
|
// Command run just before running open /final/path
|
||||||
|
NSString *preOpenCmd = @"";
|
||||||
|
|
||||||
|
NSString *quotedDestinationPath = ShellQuotedString(destinationPath);
|
||||||
|
|
||||||
|
// OS X >=10.5:
|
||||||
|
// Before we launch the new app, clear xattr:com.apple.quarantine to avoid
|
||||||
|
// duplicate "scary file from the internet" dialog.
|
||||||
|
if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_5) {
|
||||||
|
// Add the -r flag on 10.6
|
||||||
|
preOpenCmd = [NSString stringWithFormat:@"/usr/bin/xattr -d -r com.apple.quarantine %@", quotedDestinationPath];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
preOpenCmd = [NSString stringWithFormat:@"/usr/bin/xattr -d com.apple.quarantine %@", quotedDestinationPath];
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString *script = [NSString stringWithFormat:@"(while /bin/kill -0 %d >&/dev/null; do /bin/sleep 0.1; done; %@; /usr/bin/open %@) &", pid, preOpenCmd, quotedDestinationPath];
|
||||||
|
|
||||||
|
[NSTask launchedTaskWithLaunchPath:@"/bin/sh" arguments:[NSArray arrayWithObjects:@"-c", script, nil]];
|
||||||
|
}
|
16
distribution/osx/Launcher/src/compile.sh
Normal file
16
distribution/osx/Launcher/src/compile.sh
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# -fobjc-arc: enables ARC
|
||||||
|
# -fmodules: enables modules so you can import with `@import AppKit;`
|
||||||
|
# -mmacosx-version-min=10.6: support older OS X versions, this might increase the binary size
|
||||||
|
|
||||||
|
if [ ! -d "../dist" ]; then mkdir ../dist; fi
|
||||||
|
|
||||||
|
clang PFMoveApplication.m -fno-objc-arc -fmodules -mmacosx-version-min=10.6 -c -o PFMoveApplication.o
|
||||||
|
clang run-with-mono.m Launcher.m PFMoveApplication.o -fobjc-arc -fmodules -mmacosx-version-min=10.6 -o ../dist/Launcher
|
||||||
|
rm PFMoveApplication.o
|
||||||
|
|
||||||
|
if [ "$1" == "install" ] && [ "$2" != "" ]; then
|
||||||
|
echo "Installing to $2"
|
||||||
|
cp ../dist/Launcher $2
|
||||||
|
chmod +x $2
|
||||||
|
fi
|
11
distribution/osx/Launcher/src/run-with-mono.h
Normal file
11
distribution/osx/Launcher/src/run-with-mono.h
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
@import Foundation;
|
||||||
|
@import AppKit;
|
||||||
|
|
||||||
|
@interface RunWithMono : NSObject {
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void) openDownloadLink:(NSButton*)button;
|
||||||
|
+ (bool) showDownloadMonoDialog:(NSString *)appName major:(int)major minor:(int)minor;
|
||||||
|
+ (int) runAssemblyWithMono:(NSString *)appName procnamesuffix:(NSString *)procnamesuffix assembly:(NSString *)assembly major:(int) major minor:(int) minor;
|
||||||
|
|
||||||
|
@end
|
258
distribution/osx/Launcher/src/run-with-mono.m
Normal file
258
distribution/osx/Launcher/src/run-with-mono.m
Normal file
|
@ -0,0 +1,258 @@
|
||||||
|
#import "run-with-mono.h"
|
||||||
|
|
||||||
|
@import Foundation;
|
||||||
|
@import AppKit;
|
||||||
|
|
||||||
|
NSString * const VERSION_TITLE = @"Cannot launch %@";
|
||||||
|
NSString * const VERSION_MSG = @"%@ requires the Mono Framework version %d.%d or later.";
|
||||||
|
NSString * const DOWNLOAD_URL = @"http://www.mono-project.com/download/stable/#download-mac";
|
||||||
|
|
||||||
|
// Helper method to see if the user has requested debug output
|
||||||
|
bool D() {
|
||||||
|
NSString* v = [[[NSProcessInfo processInfo]environment]objectForKey:@"DEBUG"];
|
||||||
|
if (v == nil || v.length == 0 || [v isEqual:@"0"] || [v isEqual:@"false"] || [v isEqual:@"f"])
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrapper method to invoke commandline operations and return the string output
|
||||||
|
NSString *runCommand(NSString *program, NSArray<NSString *> *arguments) {
|
||||||
|
NSPipe *pipe = [NSPipe pipe];
|
||||||
|
NSFileHandle *file = pipe.fileHandleForReading;
|
||||||
|
|
||||||
|
NSTask *task = [[NSTask alloc] init];
|
||||||
|
task.launchPath = program;
|
||||||
|
task.arguments = arguments;
|
||||||
|
task.standardOutput = pipe;
|
||||||
|
|
||||||
|
[task launch];
|
||||||
|
|
||||||
|
NSData *data = [file readDataToEndOfFile];
|
||||||
|
[file closeFile];
|
||||||
|
[task waitUntilExit];
|
||||||
|
|
||||||
|
NSString *cmdOutput = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
|
||||||
|
if (cmdOutput == nil || cmdOutput.length == 0)
|
||||||
|
return nil;
|
||||||
|
|
||||||
|
return [cmdOutput stringByTrimmingCharactersInSet:
|
||||||
|
[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if the Mono version is greater than or equal to the desired version
|
||||||
|
bool isValidMono(NSString *mono, int major, int minor) {
|
||||||
|
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||||
|
|
||||||
|
if (mono == nil)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (![fileManager fileExistsAtPath:mono] || ![fileManager isExecutableFileAtPath:mono])
|
||||||
|
return false;
|
||||||
|
|
||||||
|
NSString *versionInfo = runCommand(mono, @[@"--version"]);
|
||||||
|
|
||||||
|
NSRange rg = [versionInfo rangeOfString:@"Mono JIT compiler version \\d+\\.\\d+" options:NSRegularExpressionSearch];
|
||||||
|
if (rg.location != NSNotFound) {
|
||||||
|
versionInfo = [versionInfo substringWithRange:rg];
|
||||||
|
if (D()) NSLog(@"Matched version: %@", versionInfo);
|
||||||
|
rg = [versionInfo rangeOfString:@"\\d+\\.\\d+" options:NSRegularExpressionSearch];
|
||||||
|
if (rg.location != NSNotFound) {
|
||||||
|
versionInfo = [versionInfo substringWithRange:rg];
|
||||||
|
if (D()) NSLog(@"Matched version: %@", versionInfo);
|
||||||
|
|
||||||
|
NSArray<NSString *> *versionComponents = [versionInfo componentsSeparatedByString:@"."];
|
||||||
|
if ([versionComponents[0] intValue] < major)
|
||||||
|
return false;
|
||||||
|
if ([versionComponents[0] intValue] == major && [versionComponents[1] intValue] < minor)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempts to locate a mono with a valid version
|
||||||
|
NSString *findMono(int major, int minor) {
|
||||||
|
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||||
|
|
||||||
|
NSString *currentMono = runCommand(@"/usr/bin/which", @[@"mono"]);
|
||||||
|
if (D()) NSLog(@"which mono: %@", currentMono);
|
||||||
|
|
||||||
|
if (isValidMono(currentMono, major, minor)) {
|
||||||
|
if (D()) NSLog(@"Found mono with: %@", currentMono);
|
||||||
|
return currentMono;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSArray *probepaths = @[@"/usr/local/bin/mono", @"/Library/Frameworks/Mono.framework/Versions/Current/bin/mono", @"/opt/local/bin/mono"];
|
||||||
|
for(NSString* probepath in probepaths) {
|
||||||
|
if (D()) NSLog(@"Trying mono with: %@", probepath);
|
||||||
|
if (isValidMono(probepath, major, minor)) {
|
||||||
|
if (D()) NSLog(@"Found mono with: %@", probepath);
|
||||||
|
return probepath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (D()) NSLog(@"Failed to find Mono, returning: %@", nil);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check Bundle for quarantine
|
||||||
|
void checkBundle() {
|
||||||
|
|
||||||
|
NSString * const bundlePath = [[NSBundle mainBundle] bundlePath];
|
||||||
|
NSString * const attributes = runCommand(@"/usr/bin/xattr", @[@"-l", bundlePath]);
|
||||||
|
if (D()) NSLog(@"Attributes: %@", attributes);
|
||||||
|
if ([attributes containsString:@"com.apple.quarantine:"]) {
|
||||||
|
runCommand(@"/usr/bin/xattr", @[@"-dr", @"com.apple.quarantine", bundlePath]);
|
||||||
|
NSLog(@"Removed quarantine attribute from bundle");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@implementation RunWithMono
|
||||||
|
|
||||||
|
+ (void) openDownloadLink:(NSButton*)button {
|
||||||
|
if (D()) NSLog(@"Clicked Download");
|
||||||
|
runCommand(@"/usr/bin/open", @[DOWNLOAD_URL]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shows the download dialog, prompting to download Mono
|
||||||
|
+ (bool) showDownloadMonoDialog:(NSString *)appName major:(int)major minor:(int)minor {
|
||||||
|
NSAlert *alert = [[NSAlert alloc] init];
|
||||||
|
|
||||||
|
[alert setInformativeText:[NSString stringWithFormat:VERSION_MSG, appName, major, minor]];
|
||||||
|
[alert setMessageText:[NSString stringWithFormat:VERSION_TITLE, appName]];
|
||||||
|
[alert addButtonWithTitle:@"Cancel"];
|
||||||
|
[alert addButtonWithTitle:@"Retry"];
|
||||||
|
[alert addButtonWithTitle:@"Download"];
|
||||||
|
|
||||||
|
NSButton *downloadButton = [[alert buttons] objectAtIndex:2];
|
||||||
|
|
||||||
|
[downloadButton setTarget:self];
|
||||||
|
[downloadButton setAction:@selector(openDownloadLink:)];
|
||||||
|
|
||||||
|
NSModalResponse btn = [alert runModal];
|
||||||
|
if (btn == NSAlertFirstButtonReturn) {
|
||||||
|
if (D()) NSLog(@"Clicked Cancel");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (btn == NSAlertSecondButtonReturn) {
|
||||||
|
if (D()) NSLog(@"Clicked Retry");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Top-level method, finds Mono with an appropriate version and launches the assembly
|
||||||
|
+ (int) runAssemblyWithMono: (NSString *)appName procnamesuffix:(NSString *)procnamesuffix assembly:(NSString *)assembly major:(int) major minor:(int) minor {
|
||||||
|
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||||
|
|
||||||
|
NSString *assemblyPath;
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
NSString *localPath = NSProcessInfo.processInfo.arguments[0].stringByDeletingLastPathComponent;
|
||||||
|
NSString *resourcePath = [[NSBundle mainBundle] resourcePath];
|
||||||
|
NSArray *paths = @[
|
||||||
|
localPath,
|
||||||
|
[NSString pathWithComponents:@[localPath, @"bin"]],
|
||||||
|
resourcePath,
|
||||||
|
[NSString pathWithComponents:@[resourcePath, @"bin"]]
|
||||||
|
];
|
||||||
|
for (NSString* entryFolder in paths) {
|
||||||
|
if (D()) NSLog(@"Checking folder: %@", entryFolder);
|
||||||
|
|
||||||
|
assemblyPath = [NSString pathWithComponents:@[entryFolder, assembly]];
|
||||||
|
|
||||||
|
if ([fileManager fileExistsAtPath:assemblyPath]) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
NSLog(@"Assembly file not found");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (D()) NSLog(@"assemblyPath: %@", assemblyPath);
|
||||||
|
|
||||||
|
checkBundle();
|
||||||
|
|
||||||
|
NSString *currentMono = findMono(major, minor);
|
||||||
|
|
||||||
|
while (currentMono == nil) {
|
||||||
|
NSLog(@"No valid mono found!");
|
||||||
|
bool close = [self showDownloadMonoDialog:appName major:major minor:minor];
|
||||||
|
if (close)
|
||||||
|
return 1;
|
||||||
|
currentMono = findMono(major, minor);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup dylib fallback loading
|
||||||
|
NSMutableArray * dylibPath = [NSMutableArray arrayWithObject:assemblyPath.stringByDeletingLastPathComponent];
|
||||||
|
|
||||||
|
// Update the PATH to use the specified mono version
|
||||||
|
if ([currentMono hasPrefix:@"/"])
|
||||||
|
{
|
||||||
|
NSString * curMonoBinDir = currentMono.stringByDeletingLastPathComponent;
|
||||||
|
NSString * curMonoDir = curMonoBinDir.stringByDeletingLastPathComponent;
|
||||||
|
NSString * curMonoLibDir = [NSString pathWithComponents:@[curMonoDir, @"lib"]];
|
||||||
|
|
||||||
|
NSString * curEnvPath = [NSString stringWithUTF8String:getenv("PATH")];
|
||||||
|
NSString * newEnvPath = [NSString stringWithFormat:@"%@:%@", curMonoBinDir, curEnvPath];
|
||||||
|
setenv("PATH", newEnvPath.UTF8String, 1);
|
||||||
|
|
||||||
|
[dylibPath addObject:curMonoLibDir];
|
||||||
|
|
||||||
|
NSLog(@"Added %@ to PATH", curMonoBinDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup libsqlite?
|
||||||
|
/* if [[ -f '/opt/local/lib/libsqlite3.0.dylib' ]]; then
|
||||||
|
export DYLD_FALLBACK_LIBRARY_PATH="/opt/local/lib:$DYLD_FALLBACK_LIBRARY_PATH"
|
||||||
|
fi
|
||||||
|
*/
|
||||||
|
|
||||||
|
[dylibPath addObjectsFromArray:@[@"$HOME/lib", @"/usr/local/lib", @"/lib", @"/usr/lib"]];
|
||||||
|
|
||||||
|
setenv("DYLD_FALLBACK_LIBRARY_PATH", [dylibPath componentsJoinedByString:@":"].UTF8String, 1);
|
||||||
|
|
||||||
|
if (D()) NSLog(@"Running %@ --debug %@", currentMono, assemblyPath);
|
||||||
|
|
||||||
|
// Copy commandline arguments
|
||||||
|
NSMutableArray* arguments = [[NSMutableArray alloc] init];
|
||||||
|
// Disabled suffix for now coz it's confusing and not preserved on in-app restart
|
||||||
|
[arguments addObject:currentMono];
|
||||||
|
//[arguments addObject:[currentMono stringByAppendingString:procnamesuffix]];
|
||||||
|
[arguments addObject:@"--debug"];
|
||||||
|
[arguments addObjectsFromArray:[[NSProcessInfo processInfo] arguments]];
|
||||||
|
|
||||||
|
// replace the executable-path with the assembly path
|
||||||
|
[arguments replaceObjectAtIndex:2 withObject:assemblyPath];
|
||||||
|
|
||||||
|
// Try switch to mono using execv
|
||||||
|
char * cPath = strdup([currentMono UTF8String]);
|
||||||
|
char ** cArgs;
|
||||||
|
char ** pArgNext = cArgs = malloc(sizeof(*cArgs) * ([arguments count] + 1));
|
||||||
|
for (NSString *s in arguments) {
|
||||||
|
*pArgNext++ = strdup([s UTF8String]);
|
||||||
|
}
|
||||||
|
*pArgNext = NULL;
|
||||||
|
int ret = execv(cPath, cArgs);
|
||||||
|
if (ret != 0)
|
||||||
|
NSLog(@"Failed execv with errno @d", errno);
|
||||||
|
// execv failed, cleanup
|
||||||
|
pArgNext = cArgs;
|
||||||
|
for (NSString *s in arguments) {
|
||||||
|
free(*pArgNext++);
|
||||||
|
}
|
||||||
|
free(cArgs);
|
||||||
|
free(cPath);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
|
@ -4,4 +4,4 @@ echo ##teamcity[progressStart 'Building setup file']
|
||||||
inno\ISCC.exe sonarr.iss
|
inno\ISCC.exe sonarr.iss
|
||||||
echo ##teamcity[progressFinish 'Building setup file']
|
echo ##teamcity[progressFinish 'Building setup file']
|
||||||
|
|
||||||
echo ##teamcity[publishArtifacts 'setup\output\*.exe']
|
echo ##teamcity[publishArtifacts 'distribution\windows\setup\output\*.exe']
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
@ -48,8 +48,8 @@ Name: "startupShortcut"; Description: "Create shortcut in Startup folder (Starts
|
||||||
Name: "none"; Description: "Do not start automatically"; GroupDescription: "Start automatically"; Flags: exclusive unchecked
|
Name: "none"; Description: "Do not start automatically"; GroupDescription: "Start automatically"; Flags: exclusive unchecked
|
||||||
|
|
||||||
[Files]
|
[Files]
|
||||||
Source: "..\_output_windows\Sonarr.exe"; DestDir: "{app}"; Flags: ignoreversion
|
Source: "..\..\..\_output_windows\Sonarr.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
Source: "..\_output_windows\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
|
Source: "..\..\..\_output_windows\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||||
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
|
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
|
||||||
|
|
||||||
[Icons]
|
[Icons]
|
|
@ -359,6 +359,10 @@ namespace NzbDrone.Common.Processes
|
||||||
{
|
{
|
||||||
return ("mono", $"--debug {path} {args}");
|
return ("mono", $"--debug {path} {args}");
|
||||||
}
|
}
|
||||||
|
if (OsInfo.IsOsx && path.EndsWith(".app", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
{
|
||||||
|
return ("/usr/bin/open", $"--new {path} --args {args}");
|
||||||
|
}
|
||||||
|
|
||||||
if (OsInfo.IsWindows && path.EndsWith(".bat", StringComparison.InvariantCultureIgnoreCase))
|
if (OsInfo.IsWindows && path.EndsWith(".bat", StringComparison.InvariantCultureIgnoreCase))
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
using System.Threading;
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
using NLog;
|
using NLog;
|
||||||
|
using NzbDrone.Common.Disk;
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
using NzbDrone.Common.Processes;
|
using NzbDrone.Common.Processes;
|
||||||
|
|
||||||
|
@ -14,13 +16,15 @@ namespace NzbDrone.Host
|
||||||
{
|
{
|
||||||
private readonly IRuntimeInfo _runtimeInfo;
|
private readonly IRuntimeInfo _runtimeInfo;
|
||||||
private readonly IProcessProvider _processProvider;
|
private readonly IProcessProvider _processProvider;
|
||||||
|
private readonly IDiskProvider _diskProvider;
|
||||||
private readonly IStartupContext _startupContext;
|
private readonly IStartupContext _startupContext;
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
public SpinService(IRuntimeInfo runtimeInfo, IProcessProvider processProvider, IStartupContext startupContext, Logger logger)
|
public SpinService(IRuntimeInfo runtimeInfo, IProcessProvider processProvider, IDiskProvider diskProvider, IStartupContext startupContext, Logger logger)
|
||||||
{
|
{
|
||||||
_runtimeInfo = runtimeInfo;
|
_runtimeInfo = runtimeInfo;
|
||||||
_processProvider = processProvider;
|
_processProvider = processProvider;
|
||||||
|
_diskProvider = diskProvider;
|
||||||
_startupContext = startupContext;
|
_startupContext = startupContext;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
@ -38,8 +42,31 @@ namespace NzbDrone.Host
|
||||||
{
|
{
|
||||||
var restartArgs = GetRestartArgs();
|
var restartArgs = GetRestartArgs();
|
||||||
|
|
||||||
_logger.Info("Attempting restart with arguments: {0}", restartArgs);
|
var path = _runtimeInfo.ExecutingApplication;
|
||||||
_processProvider.SpawnNewProcess(_runtimeInfo.ExecutingApplication, restartArgs);
|
var installationFolder = Path.GetDirectoryName(path);
|
||||||
|
|
||||||
|
_logger.Info("Attempting restart with arguments: {0} {1}", path, restartArgs);
|
||||||
|
|
||||||
|
if (OsInfo.IsOsx)
|
||||||
|
{
|
||||||
|
if (installationFolder.EndsWith(".app/Contents/MacOS/bin"))
|
||||||
|
{
|
||||||
|
// New MacOS App stores Sonarr binaries in MacOS/bin and has a shim in MacOS
|
||||||
|
// Run the app bundle instead of the binary
|
||||||
|
path = Path.GetDirectoryName(installationFolder);
|
||||||
|
path = Path.GetDirectoryName(path);
|
||||||
|
path = Path.GetDirectoryName(path);
|
||||||
|
}
|
||||||
|
else if (installationFolder.EndsWith(".app/Contents/MacOS"))
|
||||||
|
{
|
||||||
|
// Old MacOS App stores Sonarr binaries in MacOS together with shell script
|
||||||
|
// Run the app bundle instead
|
||||||
|
path = Path.GetDirectoryName(installationFolder);
|
||||||
|
path = Path.GetDirectoryName(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_processProvider.SpawnNewProcess(path, restartArgs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -113,10 +113,24 @@ namespace NzbDrone.Update.UpdateEngine
|
||||||
_logger.Info("Copying new files to target folder");
|
_logger.Info("Copying new files to target folder");
|
||||||
_diskTransferService.MirrorFolder(_appFolderInfo.GetUpdatePackageFolder(), installationFolder);
|
_diskTransferService.MirrorFolder(_appFolderInfo.GetUpdatePackageFolder(), installationFolder);
|
||||||
|
|
||||||
// Set executable flag on Sonarr app
|
// Handle OSX package update and set executable flag on Sonarr app
|
||||||
if (OsInfo.IsOsx)
|
if (OsInfo.IsOsx)
|
||||||
{
|
{
|
||||||
_diskProvider.SetPermissions(Path.Combine(installationFolder, "Sonarr"), "0755", null, null);
|
var shimPath = Path.Combine(installationFolder, "Sonarr");
|
||||||
|
var realShimPath = Path.Combine(installationFolder, "../Sonarr");
|
||||||
|
|
||||||
|
if (installationFolder.EndsWith("/MacOS/bin") && _diskProvider.FileExists(realShimPath))
|
||||||
|
{
|
||||||
|
// New MacOS App stores Sonarr binaries in MacOS/bin and has a shim in MacOS
|
||||||
|
// Delete the shim in the downloaded update, we shouldn't update the shim unnecessarily
|
||||||
|
_diskProvider.DeleteFile(shimPath);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Old MacOS App stores Sonarr binaries in MacOS together with shell script
|
||||||
|
// Make shim executable
|
||||||
|
_diskProvider.SetPermissions(shimPath, "0755", null, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
|
|
@ -2,6 +2,7 @@ using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common;
|
using NzbDrone.Common;
|
||||||
|
using NzbDrone.Common.Disk;
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
using NzbDrone.Common.Processes;
|
using NzbDrone.Common.Processes;
|
||||||
using IServiceProvider = NzbDrone.Common.IServiceProvider;
|
using IServiceProvider = NzbDrone.Common.IServiceProvider;
|
||||||
|
@ -18,19 +19,21 @@ namespace NzbDrone.Update.UpdateEngine
|
||||||
private readonly IServiceProvider _serviceProvider;
|
private readonly IServiceProvider _serviceProvider;
|
||||||
private readonly IProcessProvider _processProvider;
|
private readonly IProcessProvider _processProvider;
|
||||||
private readonly IStartupContext _startupContext;
|
private readonly IStartupContext _startupContext;
|
||||||
|
private readonly IDiskProvider _diskProvider;
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
public StartNzbDrone(IServiceProvider serviceProvider, IProcessProvider processProvider, IStartupContext startupContext, Logger logger)
|
public StartNzbDrone(IServiceProvider serviceProvider, IProcessProvider processProvider, IStartupContext startupContext, IDiskProvider diskProvider, Logger logger)
|
||||||
{
|
{
|
||||||
_serviceProvider = serviceProvider;
|
_serviceProvider = serviceProvider;
|
||||||
_processProvider = processProvider;
|
_processProvider = processProvider;
|
||||||
_startupContext = startupContext;
|
_startupContext = startupContext;
|
||||||
|
_diskProvider = diskProvider;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Start(AppType appType, string installationFolder)
|
public void Start(AppType appType, string installationFolder)
|
||||||
{
|
{
|
||||||
_logger.Info("Starting NzbDrone");
|
_logger.Info("Starting Sonarr");
|
||||||
if (appType == AppType.Service)
|
if (appType == AppType.Service)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -40,7 +43,7 @@ namespace NzbDrone.Update.UpdateEngine
|
||||||
}
|
}
|
||||||
catch (InvalidOperationException e)
|
catch (InvalidOperationException e)
|
||||||
{
|
{
|
||||||
_logger.Warn(e, "Couldn't start NzbDrone Service (Most likely due to permission issues). falling back to console.");
|
_logger.Warn(e, "Couldn't start Sonarr Service (Most likely due to permission issues). falling back to console.");
|
||||||
StartConsole(installationFolder);
|
StartConsole(installationFolder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,7 +59,7 @@ namespace NzbDrone.Update.UpdateEngine
|
||||||
|
|
||||||
private void StartService()
|
private void StartService()
|
||||||
{
|
{
|
||||||
_logger.Info("Starting NzbDrone service");
|
_logger.Info("Starting Sonarr service");
|
||||||
_serviceProvider.Start(ServiceProvider.SERVICE_NAME);
|
_serviceProvider.Start(ServiceProvider.SERVICE_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,6 +83,25 @@ namespace NzbDrone.Update.UpdateEngine
|
||||||
_startupContext.Flags.Add(StartupContext.NO_BROWSER);
|
_startupContext.Flags.Add(StartupContext.NO_BROWSER);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (OsInfo.IsOsx)
|
||||||
|
{
|
||||||
|
if (installationFolder.EndsWith(".app/Contents/MacOS/bin"))
|
||||||
|
{
|
||||||
|
// New MacOS App stores Sonarr binaries in MacOS/bin and has a shim in MacOS
|
||||||
|
// Run the app bundle instead
|
||||||
|
path = Path.GetDirectoryName(installationFolder);
|
||||||
|
path = Path.GetDirectoryName(path);
|
||||||
|
path = Path.GetDirectoryName(path);
|
||||||
|
}
|
||||||
|
else if (installationFolder.EndsWith(".app/Contents/MacOS"))
|
||||||
|
{
|
||||||
|
// Old MacOS App stores Sonarr binaries in MacOS together with shell script
|
||||||
|
// Run the app bundle instead
|
||||||
|
path = Path.GetDirectoryName(installationFolder);
|
||||||
|
path = Path.GetDirectoryName(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_processProvider.SpawnNewProcess(path, _startupContext.PreservedArguments);
|
_processProvider.SpawnNewProcess(path, _startupContext.PreservedArguments);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue